Geographic Math

3 endpoints for spatial calculations: multi-point distance, geographic bounding boxes, and centroid computation.

Endpoint Purpose
POST /v1/geo/distance Distance along a chain of two or more lat/lon points
POST /v1/geo/bounding-box Bounding box from a center point and radius
POST /v1/geo/center Geographic centroid of multiple coordinates

Python SDK Examples

Calculate distance between two cities

distance accepts a list of {"lat": float, "lon": float} points (minimum 2) and returns the total distance plus per-leg breakdown.

from toolkitapi import Geo

with Geo(api_key="tk_...") as geo:
    result = geo.distance(
        points=[
            {"lat": 51.5074, "lon": -0.1278},   # London
            {"lat": 48.8566, "lon":  2.3522},   # Paris
        ],
        unit="km",
    )
    print(result["total_distance"])   # ~341.38
    print(result["unit"])             # "km"
    print(result["method"])           # "haversine"

Multi-leg route distance

Pass more than 2 points to calculate a route with per-leg distances:

from toolkitapi import Geo

# A → B → C → D road trip
route = [
    {"lat": 40.7128,  "lon": -74.0060},   # New York
    {"lat": 41.8781,  "lon": -87.6298},   # Chicago
    {"lat": 39.7392,  "lon": -104.9903},  # Denver
    {"lat": 34.0522,  "lon": -118.2437},  # Los Angeles
]

with Geo(api_key="tk_...") as geo:
    result = geo.distance(route, unit="mi")

    print(f"Total distance: {result['total_distance']} mi")
    for i, leg in enumerate(result["legs"], start=1):
        print(f"  Leg {i}: {leg['distance']} mi")

Vincenty vs Haversine

Haversine is the default and works well for most applications. Use Vincenty for higher accuracy with antipodal or very short distances:

from toolkitapi import Geo

points = [
    {"lat": -33.8688, "lon": 151.2093},   # Sydney
    {"lat": 51.5074,  "lon": -0.1278},    # London
]

with Geo(api_key="tk_...") as geo:
    haversine = geo.distance(points, unit="km", method="haversine")
    vincenty  = geo.distance(points, unit="km", method="vincenty")

    print(f"Haversine: {haversine['total_distance']} km")
    print(f"Vincenty:  {vincenty['total_distance']} km")

Calculate a bounding box

bounding_box is useful for database queries ("find all records within X km of this point") — pass a center coordinate and radius to get min_lat, max_lat, min_lon, max_lon.

from toolkitapi import Geo

with Geo(api_key="tk_...") as geo:
    bbox = geo.bounding_box(
        lat=51.5074,    # London
        lon=-0.1278,
        radius=50,
        unit="km",
    )

    print(bbox["min_lat"])   # ~51.057
    print(bbox["max_lat"])   # ~51.957
    print(bbox["min_lon"])   # ~-1.023
    print(bbox["max_lon"])   # ~0.873

# Use with a database query:
# SELECT * FROM locations
# WHERE lat BETWEEN %(min_lat)s AND %(max_lat)s
# AND lon BETWEEN %(min_lon)s AND %(max_lon)s

Filter records near a location

from toolkitapi import Geo

shops = [
    {"name": "Central Store",   "lat": 51.5100, "lon": -0.1200},
    {"name": "East Branch",     "lat": 51.5200, "lon":  0.0100},
    {"name": "North Outpost",   "lat": 51.7000, "lon": -0.1500},
]

with Geo(api_key="tk_...") as geo:
    bbox = geo.bounding_box(lat=51.5074, lon=-0.1278, radius=10, unit="km")

nearby = [
    s for s in shops
    if bbox["min_lat"] <= s["lat"] <= bbox["max_lat"]
    and bbox["min_lon"] <= s["lon"] <= bbox["max_lon"]
]
print([s["name"] for s in nearby])
# ["Central Store", "East Branch"]

Find the geographic center of a set of points

center computes the true geographic centroid (using Cartesian averaging to handle the anti-meridian correctly):

from toolkitapi import Geo

# Find the center of a fleet of delivery vehicles
vehicles = [
    {"lat": 51.5100, "lon": -0.1200},
    {"lat": 51.4900, "lon": -0.1500},
    {"lat": 51.5300, "lon": -0.0900},
    {"lat": 51.5050, "lon": -0.1350},
]

with Geo(api_key="tk_...") as geo:
    result = geo.center(vehicles)
    print(result["center"]["lat"])   # ~51.509
    print(result["center"]["lon"])   # ~-0.124

Center + bounding box together

A common pattern: find the centroid of a set of points and draw a bounding box around it:

from toolkitapi import Geo

locations = [
    {"lat": 52.3676, "lon":  4.9041},  # Amsterdam
    {"lat": 50.8503, "lon":  4.3517},  # Brussels
    {"lat": 51.5074, "lon": -0.1278},  # London
    {"lat": 48.8566, "lon":  2.3522},  # Paris
]

with Geo(api_key="tk_...") as geo:
    centroid = geo.center(locations)
    lat = centroid["center"]["lat"]
    lon = centroid["center"]["lon"]

    # 250 km box around the centroid
    bbox = geo.bounding_box(lat=lat, lon=lon, radius=250, unit="km")

print(f"Center: {lat:.4f}, {lon:.4f}")
print(f"Bounding box: {bbox['min_lat']:.3f} to {bbox['max_lat']:.3f} lat")

Response Fields

distance response:

Field Type Description
total_distance float Sum of all leg distances
unit string Distance unit (km, mi, m, nm)
method string haversine or vincenty
legs list Per-leg breakdown with from_point, to_point, distance

bounding-box response:

Field Type Description
min_lat float Southern boundary
max_lat float Northern boundary
min_lon float Western boundary
max_lon float Eastern boundary
center object Input center point {lat, lon}
radius float Input radius
unit string km or mi

center response:

Field Type Description
center object Computed centroid {lat, lon}

Tip

Use bounding_box to quickly filter candidate rows from a database before running more expensive exact distance checks. The box overcounts slightly (it's a rectangle, not a circle), but it's fast to query with indexed lat/lon columns.