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.