Bounding-box radius filtering — using lat BETWEEN and lng BETWEEN — is a rectangle, not a circle. At a 5km search radius, the corners of the bounding box extend approximately 7km from the center, meaning listings up to 40% farther than the stated radius appear in results. CWE-682 (Incorrect Calculation) applies because the distance calculation is mathematically wrong. ISO 25010:2011 functional-suitability is violated because the feature does not perform as advertised. For time-sensitive searches (finding an open restaurant, locating a nearby service), returning listings that are materially farther than the stated radius causes users to navigate to incorrect distances.
High because bounding-box distance calculation is mathematically incorrect and silently returns listings outside the stated radius, producing demonstrably wrong search results.
Replace bounding-box filtering with either a PostGIS spatial query or an in-application haversine distance calculation. PostGIS is preferred for large datasets because the computation happens in the database with a spatial index.
-- PostGIS: returns listings within 5km of a point
SELECT id, name, location,
ST_Distance(location, ST_Point(:lng, :lat)::geography) AS distance_meters
FROM listings
WHERE ST_Distance(location, ST_Point(:lng, :lat)::geography) <= 5000
ORDER BY distance_meters;
For application-layer calculation when PostGIS is not available:
function haversineKm(lat1: number, lon1: number, lat2: number, lon2: number): number {
const R = 6371;
const dLat = (lat2 - lat1) * Math.PI / 180;
const dLon = (lon2 - lon1) * Math.PI / 180;
const a = Math.sin(dLat / 2) ** 2 +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLon / 2) ** 2;
return R * 2 * Math.asin(Math.sqrt(a));
}
const inRadius = listings.filter(l => haversineKm(searchLat, searchLng, l.lat, l.lng) <= radiusKm);
See also directory-map-location.location-search.radius-in-url — radius should be reflected in the URL for shareability.
ID: directory-map-location.location-search.radius-distance-calculation
Severity: high
What to look for: Examine the search radius logic. Check whether distance is calculated using great-circle distance (haversine formula) or a spatial index query. Look for bounding-box approximations that might return incorrect results. Test a search with radius and verify that results are correctly ordered by distance.
Pass criteria: Radius search uses great-circle distance (haversine formula) or a spatial database query (PostGIS, MongoDB geospatial). Enumerate all distance calculation functions and confirm at least 1 uses haversine or spatial index. Results are accurately filtered by distance from the search point.
Fail criteria: Bounding-box approximation is used as the sole distance filter. Results include listings outside the radius, or exclude listings inside the radius. A simple lat/lng BETWEEN clause without haversine refinement does not count as pass.
Skip (N/A) when: No radius-based search feature exists.
Cross-reference: Compare with directory-map-location.location-search.radius-in-url — search radius should be both accurately calculated (this check) and reflected in the URL for shareability.
Detail on fail: "Search radius uses bounding-box (lat/lng BETWEEN), returning listings 15km away from a 5km radius search" or "No distance calculation found; search appears to use arbitrary result ordering"
Remediation: Use a spatial query with PostGIS or implement haversine distance:
SELECT id, name, location
FROM listings
WHERE ST_Distance(location, ST_Point(search_lng, search_lat)::geography) <= 5000
ORDER BY ST_Distance(location, ST_Point(search_lng, search_lat)::geography);
Or in application code:
function haversineDistance(lat1, lon1, lat2, lon2) {
const R = 6371;
const dLat = (lat2 - lat1) * Math.PI / 180;
const dLon = (lon2 - lon1) * Math.PI / 180;
const a = Math.sin(dLat / 2) ** 2 +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLon / 2) ** 2;
const c = 2 * Math.asin(Math.sqrt(a));
return R * c;
}
const resultsInRadius = listings.filter(l => {
const distance = haversineDistance(searchLat, searchLng, l.lat, l.lng);
return distance <= radiusKm;
});