Functional Weave
Code in Rust

geo.bounding-box@1.0.0

README.md

2,222 bytes · view raw

# geo.bounding-box

The smallest latitude/longitude box that contains every point within a given
distance of a centre. It is the cheap first filter for "find everything within
10 km": query the box with an index, then check the real distance
(`geo.distance`) on what comes back.

## Method

J. P. Matuschek, "Finding Points Within a Distance of a Latitude/Longitude
Using Bounding Coordinates", http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates.
The Earth is a sphere of radius 6,371,008.8 m (IUGG mean radius, as in
`geo.distance`). The angular radius is r = d / R; latitudes are centre ± r;
the longitude half-width is asin(sin r / cos lat), which is wider than r away
from the equator (about twice as wide at 60°).

## Edge cases

- **Poles.** If the circle reaches or crosses a pole, it covers every
  longitude: the box is clamped to ±90 and runs from -180 to 180. Without this
  check the asin formula is asked for asin of more than 1 and returns NaN.
- **The antimeridian.** A bound that passes ±180 comes round the other side,
  so minLng > maxLng. That is the signal that the box is two strips,
  minLng..180 and -180..maxLng; a query must OR them rather than use BETWEEN.
- **Zero distance** is the point itself. **Negative distance** is an error.
- A radius larger than the Earth gives the whole globe.

## Rounding outward

Bounds are rounded to 7 decimal places (about 1 cm), outward: the minimums
down and the maximums up, so rounding can only grow the box and never cut
off part of the circle. A value within a millionth of a step of a 7-decimal
value (about 1e-13 degrees) is taken to be that value, so a centre of
51.5074 with distance 0 stays 51.5074 rather than becoming 51.5073999 because
its binary value sits a hair below.

## Why the answers are identical in every language

As in `geo.distance`, the trigonometry is written out using only correctly
rounded IEEE 754 operations (+ − × ÷, sqrt, floor) in the same order in all
three languages, instead of calling the platform's Math.sin, which may differ
in the last bit and could tip a value across a rounding step. On 4,000 random
inputs the unrounded and rounded bounds were bit-for-bit identical in
TypeScript, Python and Rust.