Geospatial search
In this guide, you’ll use Fauna to run geospatial searches in a given area using a bounding box search pattern. The method uses latitude and longitude coordinates to find places in a radius of a provided location.
The guide uses example locations from Toronto, Canada.
Create the calculateBoundingBox()
function
To start, create a user-defined function (UDF) to calculate the minimum and maximum coordinates for the bounding box:
function calculateBoundingBox(
lat: Number,
lon: Number,
radius: Number):{
minLat: Number,
maxLat: Number,
minLon: Number,
maxLon: Number
}
{
let R = 6371
let radLat = lat * Math.PI / 180
let deltaLat = radius / R
let deltaLon = Math.asin(Math.sin(deltaLat) / Math.cos(radLat))
let minLat = lat - deltaLat * 180 / Math.PI
let maxLat = lat + deltaLat * 180 / Math.PI
let minLon = lon - deltaLon * 180 / Math.PI
let maxLon = lon + deltaLon * 180 / Math.PI
let cord = {
minLat: minLat,
maxLat: maxLat,
minLon: minLon,
maxLon: maxLon
}
cord
}
Create the haversine()
function
Next, create a haversine()
function to calculate the distance, in kilometers,
between two geographic points using their coordinates. The function uses the
Haversine formula to account for the curvature of the earth.
function haversine(
coords1: { latitude: Number, longitude: Number },
coords2: { latitude: Number, longitude: Number }): Number
{
let lat1 = coords1.latitude
let lon1 = coords1.longitude
let lat2 = coords2.latitude
let lon2 = coords2.longitude
let R = 6371
let x1 = lat2 - lat1
let dLat = x1 * Math.PI / 180
let x2 = lon2 - lon1
let dLon = x2 * Math.PI / 180
let a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1 * Math.PI / 180) *
Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon / 2) *
Math.sin(dLon / 2)
let c = 2 * Math.asin(Math.sqrt(a));
let distance = R * c
distance
}
Add sample data
Create a Location
collection:
collection Location {
history_days 0
index orderByLatLong {
values [.latitude, .longitude]
}
}
Add sample data for a few locations to the collection:
Location.create({
name: "St. Lawrence Market",
latitude: 43.6487,
longitude: -79.3716
})
Location.create({
name: "Royal Ontario Museum",
latitude: 43.6677,
longitude: -79.3948
})
Location.create({
name: "Toronto Music Garden",
latitude: 43.6366,
longitude: -79.3951
})
Location.create({
name: "High Park",
latitude: 43.6465,
longitude: -79.4637
})
Location.create({
name: "Ontario Science Centre",
latitude: 43.7163,
longitude: -79.3390
})
Location.create({
name: "Casa Loma",
latitude: 43.6780,
longitude: -79.4094
})
Location.create({
name: "Scarborough Bluffs",
latitude: 43.7064,
longitude: -79.2328
})
Location.create({
name: "Black Creek Pioneer Village",
latitude: 43.7735,
longitude: -79.5164
})
Query inside a radius
Run the following query to search for places in a 5 km radius of a defined location. The results only include places in the radius. Results are ordered by distance from the location.
let distanceKm = 5.0 // find all points within 5 km of the current location
// Define the current location
let latitude = 43.6532
let longitude = -79.3832
let boundingbox = calculateBoundingBox(latitude, longitude, distanceKm)
Location.orderByLatLong({
from: [boundingbox.minLat, boundingbox.minLon],
to: [boundingbox.maxLat, boundingbox.maxLon]
})
.where(
.longitude >= boundingbox.minLon && .longitude <= boundingbox.maxLon
)
.map(l => {
location: l,
distance: haversine(
{ latitude: l.latitude, longitude: l.longitude },
{ latitude: latitude, longitude: longitude }
)
})
.order(asc(.distance))
Results:
{
data: [
{
location: {
id: "396567265944797257",
coll: Location,
ts: Time("2099-04-30T05:27:46.260Z"),
name: "St. Lawrence Market",
latitude: 43.6487,
longitude: -79.3716
},
distance: 1.0589651226108578
},
{
location: {
id: "396631857567891533",
coll: Location,
ts: Time("2099-04-30T22:34:25.630Z"),
name: "Royal Ontario Museum",
latitude: 43.6677,
longitude: -79.3948
},
distance: 1.8628877543323947
},
{
location: {
id: "396567265945845833",
coll: Location,
ts: Time("2099-04-30T05:27:46.260Z"),
name: "Toronto Music Garden",
latitude: 43.6366,
longitude: -79.3951
},
distance: 2.0794133933697307
},
{
location: {
id: "396567265946894409",
coll: Location,
ts: Time("2099-04-30T05:27:46.260Z"),
name: "Casa Loma",
latitude: 43.678,
longitude: -79.4094
},
distance: 3.470709059384686
}
]
}
Optimizing the query
You can optimize the query in previous example. You can make
a cheap index by chunking latitude and longitude together.
Update your Location
collection to include the following index:
collection Location {
history_days 0
compute lat_floor = doc => Math.floor(doc.latitude)
index by_latitude__logitude_asc {
terms [.lat_floor]
values [.longitude]
}
}
Update the query to use the new index:
// find all points within 5 km of the current location
let distanceKm = 5.0
// Define the current location
let latitude = 43.6532
let longitude = -79.3832
let boundingbox = calculateBoundingBox(latitude, longitude, distanceKm)
let lat_range =
Set.sequence(Math.floor(boundingbox.minLat), Math.ceil(boundingbox.maxLat))
lat_range
.flatMap(lat => {
Location.by_latitude__logitude_asc(
lat * 1.0,
{ from: boundingbox.minLon, to: boundingbox.maxLon }
)
})
.map(l => {
location: l,
distance: haversine(
{ latitude: l.latitude, longitude: l.longitude },
{ latitude: latitude, longitude: longitude }
)
})
.where(.distance < distanceKm)
.order(asc(.distance))
Results:
{
data: [
{
location: {
id: "396567265944797257",
coll: Location,
ts: Time("2099-04-30T05:27:46.260Z"),
lat_floor: 43.0,
name: "St. Lawrence Market",
latitude: 43.6487,
longitude: -79.3716
},
distance: 1.0589651226108578
},
{
location: {
id: "396631857567891533",
coll: Location,
ts: Time("2099-04-30T22:34:25.630Z"),
lat_floor: 43.0,
name: "Royal Ontario Museum",
latitude: 43.6677,
longitude: -79.3948
},
distance: 1.8628877543323947
},
{
location: {
id: "396567265945845833",
coll: Location,
ts: Time("2099-04-30T05:27:46.260Z"),
lat_floor: 43.0,
name: "Toronto Music Garden",
latitude: 43.6366,
longitude: -79.3951
},
distance: 2.0794133933697307
},
{
location: {
id: "396567265946894409",
coll: Location,
ts: Time("2099-04-30T05:27:46.260Z"),
lat_floor: 43.0,
name: "Casa Loma",
latitude: 43.678,
longitude: -79.4094
},
distance: 3.470709059384686
}
]
}
Is this article helpful?
Tell Fauna how the article can be improved:
Visit Fauna's forums
or email docs@fauna.com
Thank you for your feedback!