Home

Published

- 4 min read

Latitude,longitude bounds

img of Latitude,longitude bounds

I’m currently developing an application based on maps. In that application I want to represent a set of markers. In order to do so, the map library I’m using it has a fitBounds method. However, you need to compute the bounds of the map that allow all the markers to be visible. I describe in this article the implemented algorithm.

Abstraction of latitude longitude

First of all, we need to do a nasty approximation. We can represent the earth globe, as a 2-D cartesian axis. In order to do that, we can consider latitude as the y axis and longitude as the x axis.

Longitude will be in range [-180,180] and latitude in the range [-90,90]. We can define the cardinal points in the chart:

  • North -> Point in (0,90)
  • East -> Point in (180,0)
  • South -> Point in (0,-90)
  • West -> Point in (-180,0)

We can define the bounds of a set of points using two points only: North-East point and South

  • North-East -> Point in (180,90)
  • South-West -> Point in (-180,-90)

We can see a visual representation of those points in the following chart:

Taking this into account we can set up some algorithm to calculate the bounds.

Algorithm to compute the bounds

The first basic algorithm is to iterate over all points and compare the longitude (x) and latitude (y) and obtain the point with higher x and y and the point with lower x and y.

const SW: LatLngTuple = [-90, -180]
const NE: LatLngTuple = [90, 180]
export const ALL_WORLD_BOUNDS: LatLngBoundsExpression = [NE, SW]
export const getBoundsFromPoints = (points: Point[]): LatLngBoundsExpression => {
	if (points.length === 0) return ALL_WORLD_BOUNDS

	let nex = 0,
		swx = 0,
		ney = 0,
		swy = 0
	points.forEach((point) => {
		if (nex === 0 && swx === 0 && ney === 0 && swy === 0) {
			nex = swx = point.longitude
			ney = swy = point.latitude
		} else {
			if (point.longitude > nex) nex = point.longitude
			if (point.longitude < swx) swx = point.longitude
			if (point.latitude > ney) ney = point.latitude
			if (point.latitude < swy) swy = point.latitude
		}
	})
	return [
		[ney, nex],
		[swy, swx]
	]
}

Bear in mind that the map library expects and array of [lat,lng], that’s why we are switching the natural order of x,y and we’re using y,x that corresponds to [lat,lng].

Unit test

These are the unit tests that check this algorithm behaves correctly:

import { ALL_WORLD_BOUNDS, getBoundsFromPoints } from './BoundCalculator'
import { Point } from '../../types/Point'

test('should compute bounds of an empty list', () => {
	const bounds = getBoundsFromPoints([])
	expect(bounds).toEqual(ALL_WORLD_BOUNDS)
})

test('should compute bounds of one point', () => {
	const lat = 1,
		lng = 1
	const point = new Point(lat, lng)
	const bounds = getBoundsFromPoints([point])
	expect(bounds).toEqual([
		[lat, lng],
		[lat, lng]
	])
})

test('should compute bounds a list of points, each point per quadrant', () => {
	const lat1 = 30,
		lng1 = 90
	const lat2 = 30,
		lng2 = -90
	const lat3 = -30,
		lng3 = -90
	const lat4 = -30,
		lng4 = 90
	const point1 = new Point(lat1, lng1)
	const point2 = new Point(lat2, lng2)
	const point3 = new Point(lat3, lng3)
	const point4 = new Point(lat4, lng4)
	const bounds = getBoundsFromPoints([point1, point2, point3, point4])
	expect(bounds).toEqual([
		[lat1, lng1],
		[lat3, lng3]
	])
})