Exploring Your Neighborhood: A Step-by-Step Guide to Finding Places Near You
Tags: go, harversine, backend, tutorial
Published: 2024-04-16 15:00:14

Imagine that you are working on a task where your goal is to create a function that receives a latitude, longitude, and max distance and returns places nearby. Without directions on where to get started, you decide to research how to achieve this goal. After a time you realize that you only discovered websites saying to you to use Google Maps API.

But if I tell you that there exists a mathematical formula that can help you with this task? That’s what you will learn in this tutorial.

### The Haversine Formula

The Haversine Formula is a mathematical formula used to calculate the distance between two points on a sphere, such as the Earth. It takes into account the radius of the sphere as well as the latitude and longitude of the two points and calculates the great-circle distance, which is the shortest distance between the two points along the surface of the sphere.

The Haversine formula is particularly useful for calculating distances between two points on the Earth’s surface, as it takes into account the fact that the Earth is not a perfect sphere, but rather an oblate spheroid. It is commonly used in applications such as GPS navigation, geodesy, and astronomy.

The formula is expressed as follows:

`d = 2r * arcsin(sqrt(sin^2((lat2-lat1)/2) + cos(lat1) * cos(lat2) * sin^2((lon2-lon1)/2)))`

where:

`d` = distance between the two points in units of the sphere’s radius (typically kilometers or miles)

`r` = radius of the sphere, in this case, Earth (in the same units as d)

`lat1, lat2` = latitude of point 1 and point 2 (in radians)

`lon1, lon2` = longitude of point 1 and point 2 (in radians)

### Let's code

As our goal is to create a function that calculates places nearby, I decided not to show how to initialize the project and keep the focus only on the function.

Furthermore, it could be said that I’ll be using the Go programming language for this particular tutorial.

Let’s start by creating the Place struct, the variables and constants, and the main function:

```golang
type Place struct {
	Name      string `json:"name"`
	Latitude  float64 `json:"latitude"`
	Longitude float64 `json:"longitude"`
}

const EARTH_RADIUS = 6371.0
const MAX_DISTANCE = 50.0

func main() {
	myLatitude := -27.600852
	myLongitude := -48.505479

	places := []Place{
		{Name: "Ramiro Ruediger Park", Latitude: -26.9134, Longitude: -49.0855},
		{Name: "Corrego Grande Municipal Park", Latitude: -27.5998, Longitude: -48.5114},
		{Name: "Corrego Grande Linear Park", Latitude: -27.6028, Longitude: -48.5037},
		{Name: "Coqueiros Park", Latitude: -27.6016, Longitude: -48.5745},
	}
}
```

Great, now let’s create the function that will use the Haversine Formula:

```golang
func getNearbyPlaces(latitude float64, longitude float64, maxDistance float64, places []Place) []Place {
	var nearbyPlaces []Place

	for _, place := range places {
		lat2 := radians(place.Latitude)
		long2 := radians(place.Longitude)

		dlat := lat2 - radians(latitude)
		dlong := long2 - radians(longitude)

		a := math.Pow(math.Sin(dlat/2), 2) + math.Cos(radians(latitude))*math.Cos(lat2)*math.Pow(math.Sin(dlong/2), 2)

		c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))

		distance := EARTH_RADIUS * c

		if distance <= maxDistance {
			nearbyPlaces = append(nearbyPlaces, place)
		}
	}

	return nearbyPlaces
}
```

The only missing thing that we have to create is the radians function. This function will be responsible for converting the latitude and longitude, which are in degrees units, to radians units.

```golang
func radians(degrees float64) float64 {
	return degrees * (math.Pi / 180)
}
```

And that’s it, we made it 🔥.

### Let's test

I will start by using the latitude -27.600852 and the longitude -48.505479, which is a point near Córrego Grande Municipal Park and Córrego Grande Linear Park. Furthermore, I will set the max distance of 1.5 kilometers, so I expect that the getNearbyPlaces function will return to me only Córrego Grande Municipal Park and Córrego Grande Linear Park as they are the closest places of the current latitude and longitude.

```golang
type Place struct {
	Name      string `json:"name"`
	Latitude  float64 `json:"latitude"`
	Longitude float64 `json:"longitude"`
}

const EARTH_RADIUS = 6371.0
const MAX_DISTANCE = 1.5

func main() {
	myLatitude := -27.600852
	myLongitude := -48.505479

	places := []Place{
		{Name: "Ramiro Ruediger Park", Latitude: -26.9134, Longitude: -49.0855},
		{Name: "Corrego Grande Municipal Park", Latitude: -27.5998, Longitude: -48.5114},
		{Name: "Corrego Grande Linear Park", Latitude: -27.6028, Longitude: -48.5037},
		{Name: "Coqueiros Park", Latitude: -27.6016, Longitude: -48.5745},
	}

	nearbyPlaces := getNearbyPlaces(myLatitude, myLongitude, MAX_DISTANCE, places)

	fmt.Println(nearbyPlaces)
	// [{Corrego Grande Municipal Park -27.5998 -48.5114} {Corrego Grande Linear Park -27.6028 -48.5037}]
}
```

Great, it’s working. Let’s increase the max distance to ensure that we receive also Coqueiros Park.

```golang
type Place struct {
	Name      string `json:"name"`
	Latitude  float64 `json:"latitude"`
	Longitude float64 `json:"longitude"`
}

const EARTH_RADIUS = 6371.0
const MAX_DISTANCE = 15.0

func main() {
	myLatitude := -27.600852
	myLongitude := -48.505479

	places := []Place{
		{Name: "Ramiro Ruediger Park", Latitude: -26.9134, Longitude: -49.0855},
		{Name: "Corrego Grande Municipal Park", Latitude: -27.5998, Longitude: -48.5114},
		{Name: "Corrego Grande Linear Park", Latitude: -27.6028, Longitude: -48.5037},
		{Name: "Coqueiros Park", Latitude: -27.6016, Longitude: -48.5745},
	}

	nearbyPlaces := getNearbyPlaces(myLatitude, myLongitude, MAX_DISTANCE, places)

	fmt.Println(nearbyPlaces)
	// [{Corrego Grande Municipal Park -27.5998 -48.5114} {Corrego Grande Linear Park -27.6028 -48.5037} {Coqueiros Park -27.6016 -48.5745}]
}
```

As expected, the function returned three places, Córrego Grande Municipal Park, Córrego Grande Linear Park, and Coqueiros Park. Now is your turn, copy the code, add your places, and make some tests.

<a href="https://links.luisfpedroso.com/card-devto" target="_blank" style="display: inline-block"><img alt="This is Luis Filipe Pedroso's card." src="https://cdn.hihello.me/cards/09a4ad63-231f-4e3f-8357-f9f2e1bf973f/signature_imagelogo.png?generated=1713198825569" width="360" style="display: inline-block; min-height: 100px" /></a>