# Geospatial search

In this tutorial, 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 tutorial 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,
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))``````
``````{
data: [
{
location: {
id: "396567265944797257",
coll: Location,
ts: Time("2024-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("2024-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("2024-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("2024-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))``````
``````{
data: [
{
location: {
id: "396567265944797257",
coll: Location,
ts: Time("2024-04-30T05:27:46.260Z"),
chunk: 42920.0,
name: "St. Lawrence Market",
latitude: 43.6487,
longitude: -79.3716
},
distance: 1.0589651226108578
},
{
location: {
id: "396631857567891533",
coll: Location,
ts: Time("2024-04-30T22:34:25.630Z"),
chunk: 42920.0,
name: "Royal Ontario Museum",
latitude: 43.6677,
longitude: -79.3948
},
distance: 1.8628877543323947
},
{
location: {
id: "396567265945845833",
coll: Location,
ts: Time("2024-04-30T05:27:46.260Z"),
chunk: 42920.0,
name: "Toronto Music Garden",
latitude: 43.6366,
longitude: -79.3951
},
distance: 2.0794133933697307
},
{
location: {
id: "396567265946894409",
coll: Location,
ts: Time("2024-04-30T05:27:46.260Z"),
chunk: 42920.0,
name: "Casa Loma",
latitude: 43.678,
longitude: -79.4094
},
distance: 3.470709059384686
}
]
}``````