Geo Functions
Geospatial functions for distance calculations and location-based queries.
Geospatial Functions
SDBQL provides geospatial functions for calculating distances between coordinates using the Haversine formula, which accounts for Earth's curvature.
Geometric Tests
GEO_WITHIN(point, polygon)
Returns true if the point is inside the specified polygon (ray casting). Polygon is an array of points (LinearRing).
-- Check if point is inside a zone
LET point = {lat: 48.8566, lon: 2.3522}
-- Polygon defined as array of [lon, lat] or objects
LET zone = [[2.3, 48.8], [2.4, 48.8], [2.4, 48.9], [2.3, 48.9]]
RETURN GEO_WITHIN(point, zone)
Haversine Formula
The Haversine formula calculates the great-circle distance between two points on a sphere using their latitude and longitude coordinates. This provides accurate distance calculations for most use cases on Earth.
Practical Examples
-- Find places within 10km of a location
FOR place IN places
LET dist = DISTANCE(place.lat, place.lon, 48.8566, 2.3522)
FILTER dist <= 10000
SORT dist ASC
RETURN { name: place.name, distance: ROUND(dist) }
-- Find the nearest restaurant to user
FOR restaurant IN restaurants
LET dist = DISTANCE(restaurant.lat, restaurant.lon, @userLat, @userLon)
SORT dist ASC
LIMIT 1
RETURN { name: restaurant.name, distance: ROUND(dist) }
-- Find all stores within 5km, sorted by distance
FOR store IN stores
LET dist = DISTANCE(store.location.lat, store.location.lon, @lat, @lon)
FILTER dist <= 5000
SORT dist ASC
RETURN {
name: store.name,
address: store.address,
distance_km: ROUND(dist / 1000, 2)
}
-- Group locations by distance ranges
FOR loc IN locations
LET dist = DISTANCE(loc.lat, loc.lon, @centerLat, @centerLon)
COLLECT range = CASE
WHEN dist < 1000 THEN "< 1km"
WHEN dist < 5000 THEN "1-5km"
WHEN dist < 10000 THEN "5-10km"
ELSE "> 10km"
END
WITH COUNT INTO count
RETURN { range, count }
-- Calculate delivery zone
FOR order IN orders
LET customerLoc = (FOR c IN customers FILTER c._key == order.customer_key RETURN c)[0]
LET warehouseLoc = (FOR w IN warehouses FILTER w._key == order.warehouse_key RETURN w)[0]
LET dist = DISTANCE(
customerLoc.lat, customerLoc.lon,
warehouseLoc.lat, warehouseLoc.lon
)
RETURN {
order_id: order._key,
distance_km: ROUND(dist / 1000, 1),
delivery_zone: dist < 10000 ? "local" : dist < 50000 ? "regional" : "national"
}
-- Find locations within a bounding box (approximate)
LET centerLat = 48.8566
LET centerLon = 2.3522
LET radiusKm = 10
LET latDelta = radiusKm / 111 -- approx km per degree lat
LET lonDelta = radiusKm / (111 * COS(centerLat * PI() / 180))
FOR place IN places
FILTER place.lat >= centerLat - latDelta
FILTER place.lat <= centerLat + latDelta
FILTER place.lon >= centerLon - lonDelta
FILTER place.lon <= centerLon + lonDelta
LET exactDist = DISTANCE(place.lat, place.lon, centerLat, centerLon)
FILTER exactDist <= radiusKm * 1000
SORT exactDist
RETURN { name: place.name, distance_m: ROUND(exactDist) }
Tips for Geo Queries
Performance
- Create geo indexes on lat/lon fields
- Use bounding box pre-filtering for large datasets
- Store coordinates in a consistent format
Best Practices
- Use decimal degrees (not DMS)
- Validate coordinates are within valid ranges
- Consider timezone for location-based features