Using React geolocation to perform geoqueries with Parse
Introduction
In this guide, you will perform geoqueries in Parse using the React geolocation. You will implement a React component using these queries and learn how to set up and query realistic data using Back4App and React.
If you want to run this guide’s example project, you should set up the Ant Design library.
Goal
Perform Geoqueries using GeoPoints stored on Back4App and React geolocation.
1 - Understanding the Parse.Query class
Any Parse query operation uses the Parse.Query object type, which will help you retrieve specific data from your database throughout your app. It is crucial to know that a Parse.Query will only resolve after calling a retrieve method (like Parse.Query.find or Parse.Query.get), so a query can be set up and several modifiers can be chained before actually being called.
To create a new Parse.Query, you need to pass as a parameter the desired Parse.Object subclass, which is the one that will contain your query results. An example query can be seen below, in which a fictional Profile subclass is being queried.
JS
1// This will create your query2let parseQuery =newParse.Query("Profile");3// The query will resolve only after calling this method4let queryResult =await parseQuery.find();
Let’s create a City class, which will be the target of our queries in this guide. On Parse JS Console it 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:
Go ahead and create the Cityc class with the following example content:
JS
1// Add City objects and create table2// Note how GeoPoints are created, passing latitude and longitude as arguments3// Montevideo4var City =newParse.Object('City');5 City.set('name','Montevideo - Uruguay');6 City.set('location',newParse.GeoPoint(-34.85553195363169,-56.207280375137955));7await City.save();89// Brasília10 City =newParse.Object('City');11 City.set('name','Brasília - Brazil');12 City.set('location',newParse.GeoPoint(-15.79485821477289,-47.88391074690196));13await City.save();1415// Bogotá16 City =newParse.Object('City');17 City.set('name','Bogotá - Colombia');18 City.set('location',newParse.GeoPoint(4.69139880891712,-74.06936691331047));19await City.save();2021// Mexico City22 City =newParse.Object('City');23 City.set('name','Mexico City - Mexico');24 City.set('location',newParse.GeoPoint(19.400977162618933,-99.13311378164776));25await City.save();2627// Washington, D.C.28 City =newParse.Object('City');29 City.set('name','Washington, D.C. - USA');30 City.set('location',newParse.GeoPoint(38.930727220189944,-77.04626261880388));31await City.save();3233// Ottawa34 City =newParse.Object('City');35 City.set('name','Ottawa - Canada');36 City.set('location',newParse.GeoPoint(45.41102167733425,-75.695414598736));37await City.save();3839 console.log('Success!');
3 - Query the data
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 Parse.Query.near method:
JS
1// Create your query2let parseQuery =newParse.Query('City');34// Create our GeoPoint for the query5let kingstonGeoPoint =newParse.GeoPoint(18.018086950599134,-76.79894232253473);67// `near` will order results based on distance between the GeoPoint type field from the class and the GeoPoint argument8 parseQuery.near('location', kingstonGeoPoint);910// The query will resolve only after calling this method, retrieving11// an array of `Parse.Objects`12let queryResults =await parseQuery.find();1314// Let's show the results15for(let result of queryResults){16// You access `Parse.Objects` attributes by using `.get`17 console.log(result.get('name'));18};
Let’s now query using the method Parse.Query.withinKilometers, 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.
JS
1// Create your query2let parseQuery =newParse.Query('City');34// Create our GeoPoint for the query5let kingstonGeoPoint =newParse.GeoPoint(18.018086950599134,-76.79894232253473);67// You can also use `withinMiles` and `withinRadians` the same way,8// but with different measuring unities9 parseQuery.withinKilometers('location', kingstonGeoPoint,3000);1011// The query will resolve only after calling this method, retrieving12// an array of `Parse.Objects`13let queryResults =await parseQuery.find();1415// Let's show the results16for(let result of queryResults){17// You access `Parse.Objects` attributes by using `.get`18 console.log(result.get('name'));19};
Another useful query method is Parse.Query.withinPolygon, 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.
JS
1// Create your query2let parseQuery =newParse.Query('City');34// Create our GeoPoint polygon for the query5let geoPoint1 =newParse.GeoPoint(15.822238344514378,-72.42845934415942);6let geoPoint2 =newParse.GeoPoint(-0.7433770196268968,-97.44765968406668);7let geoPoint3 =newParse.GeoPoint(-59.997149373299166,-76.52969196322749);8let geoPoint4 =newParse.GeoPoint(-9.488786415007201,-18.346101586021952);9let geoPoint5 =newParse.GeoPoint(15.414859532811047,-60.00625459569375);1011// Note that the polygon is merely an array of GeoPoint objects and that the first and last are not connected, so Parse connects them for you12 parseQuery.withinPolygon('location',[geoPoint1, geoPoint2, geoPoint3, geoPoint4, geoPoint5]);1314// The query will resolve only after calling this method, retrieving15// an array of `Parse.Objects`16let queryResults =await parseQuery.find();1718// Let's show the results19for(let result of queryResults){20// You access `Parse.Objects` attributes by using `.get`21 console.log(result.get('name'));22};
4 - Query from a React component
Let’s now use our example queries inside a component in React, with a simple interface having a list showing results and also 3 buttons for calling the queries. The component also retrieves the device’s current location using the navigator.geolocation JavaScript API, so the queries will be using real data, provided that the user didn’t disabled location access in the browser.
This is how the component code is laid out, note the doQueryfunctions, containing the example code form before.
JavaScript
TypeScript
1import React,{ useState,FC, ReactElement }from'react';2import'./App.css';3import{ Button, Divider }from'antd';4import{ CloseOutlined, SearchOutlined }from'@ant-design/icons';5const Parse =require('parse/dist/parse.min.js');67typeLocationObject={latitude:number, longitude:number};89exportconst QueryGeo:FC<{}>=(): ReactElement =>{10// State variable11const[queryResults, setQueryResults]=useState<Parse.Object[]>();1213constdoQueryNear=asyncfunction():Promise<boolean>{14// Check if geolocation API is available in the browser15if("geolocation"in navigator){16// Get current location and create the GeoPoint for the query17 navigator.geolocation.getCurrentPosition(asyncfunction(position){18const currentPosition: LocationObject ={ latitude: position.coords.latitude, longitude: position.coords.longitude };19// Create our GeoPoint20let currentLocationGeoPoint: Parse.GeoPoint =newParse.GeoPoint(21 currentPosition.latitude,22 currentPosition.longitude,23);2425// Create our query26let parseQuery: Parse.Query =newParse.Query('City');2728// `near` will order results based on distance between the GeoPoint type field from the class and the GeoPoint argument29 parseQuery.near('location', currentLocationGeoPoint);3031try{32let results: Parse.Object[]=await parseQuery.find();33// Set query results to state variable34setQueryResults(results);35returntrue;36}catch(error){37// Error can be caused by lack of Internet connection38alert(`Error! ${error.message}`);39}40},function(_error:any){41alert("You need to allow geolocation to retrieve your location on the browser to test this. If you are on an Apple OS, enable it on your system preferences.");42});43}else{44alert("Geolocation services are not supported by your browser.");45}46returnfalse;47};4849constdoQueryWithinKilometers=asyncfunction():Promise<boolean>{50// Check if geolocation API is available in the browser51if("geolocation"in navigator){52// Get current location and create the GeoPoint for the query53 navigator.geolocation.getCurrentPosition(asyncfunction(position){54const currentPosition: LocationObject ={ latitude: position.coords.latitude, longitude: position.coords.longitude };55// Create our GeoPoint56let currentLocationGeoPoint: Parse.GeoPoint =newParse.GeoPoint(57 currentPosition.latitude,58 currentPosition.longitude,59);6061// Create our query62let parseQuery: Parse.Query =newParse.Query('City');6364// You can also use `withinMiles` and `withinRadians` the same way,65// but with different measuring unities66 parseQuery.withinKilometers('location', currentLocationGeoPoint,3000);6768try{69let results: Parse.Object[]=await parseQuery.find();70// Set query results to state variable71setQueryResults(results);72}catch(error){73// Error can be caused by lack of Internet connection74alert(`Error! ${error.message}`);75}76},function(_error:any){77alert("You need to allow geolocation to retrieve your location on the browser to test this. If you are on an Apple OS, enable it on your system preferences.");78});79}else{80alert("Geolocation services are not supported by your browser.");81}82returnfalse;83};8485constdoQueryWithinPolygon=asyncfunction():Promise<boolean>{86// Create our GeoPoint polygon points87// In React TypeScript types ('@types/parse'), Parse.Query.withinPolygon expects88// an array of number arrays, so you need to change the declarations 89let geoPoint1:number[]=[90-7.242.845.934.415.940,009115.822.238.344.514.300,0092];93let geoPoint2:number[]=[94-9.744.765.968.406.660,0095-0.7433770196268968,96];97let geoPoint3:number[]=[98-7.652.969.196.322.740,0099-59.997.149.373.299.100,00100];101let geoPoint4:number[]=[102-18.346.101.586.021.900,00103-9.488.786.415.007.200,00104];105let geoPoint5:number[]=[106-6.000.625.459.569.370,0010715.414.859.532.811.000,00108];109110// Create our query111let parseQuery: Parse.Query =newParse.Query('City');112113// Note that the polygon is merely an array of GeoPoint objects and that the first and114// last are not connected, so Parse connects them for you115 parseQuery.withinPolygon('location',[116 geoPoint1,117 geoPoint2,118 geoPoint3,119 geoPoint4,120 geoPoint5,121]);122123try{124let results: Parse.Object[]=await parseQuery.find();125// Set query results to state variable126setQueryResults(results);127}catch(error){128// Error can be caused by lack of Internet connection129alert(`Error! ${error}`);130returnfalse;131}132returntrue;133};134135constclearQueryResults=asyncfunction():Promise<boolean>{136setQueryResults(undefined);137returntrue;138};139140return(141<div>142<div className="header">143<img
144 className="header_logo"145 alt="Back4App Logo"146 src={147'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'148}149/>150<p className="header_text_bold">{'React on Back4App'}</p>151<p className="header_text">{'GeoPoint Queries'}</p>152</div>153<div className="container">154<div className="flex_between">155<h2 className="heading">{'Query List'}</h2>156<div className="flex">157<Button
158 onClick={()=>doQueryNear()}159 type="primary"160 className="heading_button"161 color={'#208AEC'}162 icon={<SearchOutlined />}163>164QUERYNEAR165</Button>166<Button
167 onClick={()=>doQueryWithinKilometers()}168 type="primary"169 className="heading_button"170 color={'#208AEC'}171 icon={<SearchOutlined />}172>173QUERYWITHINKM174</Button>175<Button
176 onClick={()=>doQueryWithinPolygon()}177 type="primary"178 className="heading_button"179 color={'#208AEC'}180 icon={<SearchOutlined />}181>182QUERYWITHINPOLYGON183</Button>184<Button
185 onClick={()=>clearQueryResults()}186 type="primary"187 className="heading_button"188 color={'#208AEC'}189 icon={<CloseOutlined />}190>191CLEARRESULTS192</Button>193</div>194</div>195<Divider />196<div className="flex_between">197<div className="flex_child">198{/* Query list */}199{queryResults !==undefined&&200 queryResults.map((result: Parse.Object, index:number)=>(201<div className="list_item" key={`${index}`}>202<p className="list_item_title">{`${result.get('name')}`}</p>203</div>204))}205{queryResults !==undefined&&206 queryResults.length <=0?(207<p>{'No results here!'}</p>208):null}209</div>210</div>211</div>212</div>213);214};
Also add these classes to you App.css file to fully render the component layout:
This is how the component should look like after rendering and querying one of the query functions:
Conclusion
At the end of this guide, you learned how GeoPoint data queries work on Parse and how to perform them on Back4App from a React application. In the next guide, you will check how to create and manage users in Parse.