How to Check if a Latitude and Longitude Is Within a Circle (Radius) in PHP
Learn how to check whether a GPS coordinate lies within a given radius of a center point in PHP, using the spherical law of cosines and the Haversine formula, with production-ready, validated code.

On this page
- The Core Idea
- The Original Solution
- How the Code Works
- Limitations of the Original Approach
- A Production-Ready Version (PHP 8+)
- What Changed and Why
- Scaling Up: Checking Many Points in a Database
- Security and Privacy Considerations
- Frequently Asked Questions
- How do I check if a point is within a radius in PHP?
- Should I use the Haversine formula or the spherical law of cosines?
- How accurate is the Haversine formula?
- Why does my distance calculation return NaN?
- What Earth radius should I use?
- Conclusion
A common requirement in location-aware applications is determining whether a point lies inside a circular area. Typical examples include checking whether a delivery address falls within a service radius, whether an employee is close enough to an office to mark attendance, or whether a device has entered a geofenced zone.
In this post, I walk through the solution I originally shared on [Stack Overflow](https://stackoverflow.com/questions/68590454/check-if-a-latitude-and-longitude-is-within-a-circle-in-php), explain how it works, and then present a more robust version suitable for production systems.
The Core Idea
A "circle" on a map is defined by two things: a center point (latitude and longitude) and a radius. A point is inside the circle if its distance from the center is less than or equal to the radius.
The problem therefore reduces to one question: what is the distance between two coordinates on the surface of the Earth? Because the Earth is (approximately) a sphere, we cannot use the flat Euclidean distance formula. We need the great-circle distance, which is the shortest path between two points along the surface of a sphere.
The Original Solution
// Posted by Gowri Shankar Balasubramaniam, modified by community. See post 'Timeline' for change history
// Retrieved 2026-10-01, License - CC BY-SA 4.0
$lat1 = "10.5900627";
$lon1 = "77.1933317";
$lat2 = "10.6068507";
$lon2 = "77.117246";
function distance($lat1, $lon1, $lat2, $lon2, $unit)
{
if (($lat1 == $lat2) && ($lon1 == $lon2)) {
return 0;
} else {
$theta = $lon1 - $lon2;
$dist = sin(deg2rad($lat1)) sin(deg2rad($lat2)) + cos(deg2rad($lat1)) cos(deg2rad($lat2)) * cos(deg2rad($theta));
$dist = acos($dist);
$dist = rad2deg($dist);
$miles = $dist 60 1.1515;
$unit = strtoupper($unit);
if ($unit == "K") {
return ($miles * 1.609344);
} else if ($unit == "N") {
return ($miles * 0.8684);
} else {
return $miles;
}
}
}
if(distance($lat1, $lon1, $lat2, $lon2, "K") < 10)
{
echo "True";
} else {
echo "False";
}For the sample coordinates above, the computed distance is approximately 8.52 km, so the script prints True for a 10 km radius.
How the Code Works
The function uses the spherical law of cosines, which calculates the central angle between two points on a sphere:
Longitude difference.
$thetais the difference in longitude between the two points.Central angle. The expression
sin(φ1)·sin(φ2) + cos(φ1)·cos(φ2)·cos(Δλ)gives the cosine of the angle between the two points, measured from the center of the Earth.acos()converts it back to an angle in radians, andrad2deg()converts it to degrees.Degrees to miles. One degree of arc on a great circle equals 60 nautical miles, and one nautical mile is approximately 1.1515 statute miles. Multiplying by
60 * 1.1515therefore converts the angle into statute miles.Unit conversion. The result is converted to kilometers (
K), nautical miles (N), or left in statute miles (default).Radius check. If the distance from the center is less than the radius (10 km here), the point is inside the circle.
The early return for identical coordinates is important. It avoids passing a value fractionally greater than
1toacos(), which would returnNaN.
Limitations of the Original Approach
The original code works well for its purpose, but there are several points to consider before using it in a production system:
Floating-point edge cases. For points that are very close together but not identical, rounding errors can push the cosine value slightly above
1.0, causingacos()to returnNaN. Any comparison withNaNevaluates tofalse, so a point that is effectively at the center could be reported as outside the circle.Numerical precision at short distances. The law of cosines loses precision for small distances. The Haversine formula is numerically better conditioned and is the standard choice for this task.
No input validation. Coordinates are accepted as untyped strings. In a real application they usually come from user input, a mobile device, or an API, and must be treated as untrusted.
Boundary semantics. The check uses
<, so a point exactly on the edge of the circle is considered outside. Use<=if boundary points should count as inside.Earth model. Both formulas treat the Earth as a perfect sphere. The error relative to the true ellipsoidal shape is up to about 0.5%, which is acceptable for most geofencing use cases but not for surveying or high-precision navigation.
A Production-Ready Version (PHP 8+)
The following version uses the Haversine formula, strict typing, input validation, and clamping to guard against floating-point errors.
<?php
declare(strict_types=1);
/**
* Mean Earth radius in kilometers (IUGG value).
*/
const EARTH_RADIUS_KM = 6371.0088;
/**
* Validate and normalize a coordinate from an untrusted source.
*
* @throws InvalidArgumentException
*/
function parseCoordinate(mixed $value, float $min, float $max, string $name): float
{
$number = filter_var($value, FILTER_VALIDATE_FLOAT);
if ($number === false || !is_finite($number) || $number < $min || $number > $max) {
throw new InvalidArgumentException(sprintf('Invalid %s value.', $name));
}
return $number;
}
/**
* Great-circle distance between two points using the Haversine formula.
*/
function haversineDistanceKm(float $lat1, float $lon1, float $lat2, float $lon2): float
{
$dLat = deg2rad($lat2 - $lat1);
$dLon = deg2rad($lon2 - $lon1);
$a = sin($dLat / 2) ** 2
+ cos(deg2rad($lat1)) cos(deg2rad($lat2)) sin($dLon / 2) ** 2;
// Clamp to [0, 1] to guard against floating-point drift.
$a = min(1.0, max(0.0, $a));
return EARTH_RADIUS_KM 2 atan2(sqrt($a), sqrt(1 - $a));
}
/**
* Determine whether a point lies within (or on the edge of) a circle.
*
* @throws InvalidArgumentException
*/
function isWithinRadius(
float $centerLat,
float $centerLon,
float $pointLat,
float $pointLon,
float $radiusKm
): bool {
if (!is_finite($radiusKm) || $radiusKm < 0) {
throw new InvalidArgumentException('Radius must be a non-negative finite number.');
}
return haversineDistanceKm($centerLat, $centerLon, $pointLat, $pointLon) <= $radiusKm;
}
// Example usage with untrusted input
try {
$centerLat = parseCoordinate('10.5900627', -90.0, 90.0, 'center latitude');
$centerLon = parseCoordinate('77.1933317', -180.0, 180.0, 'center longitude');
$pointLat = parseCoordinate('10.6068507', -90.0, 90.0, 'point latitude');
$pointLon = parseCoordinate('77.117246', -180.0, 180.0, 'point longitude');
$inside = isWithinRadius($centerLat, $centerLon, $pointLat, $pointLon, 10.0);
echo $inside ? 'Inside the radius' : 'Outside the radius';
} catch (InvalidArgumentException $e) {
// Log the details internally; return a generic message to the client.
http_response_code(400);
echo 'Invalid coordinates supplied.';
}What Changed and Why
Area | Original | Improved |
|---|---|---|
Formula | Spherical law of cosines | Haversine (better precision at short distances) |
Floating-point safety | Only handles identical points | Clamps intermediate value to |
Typing | Untyped strings |
|
Input validation | None | Numeric, finite, and range checks |
Boundary handling |
|
|
Error handling | None | Exceptions with generic client-facing errors |
Scaling Up: Checking Many Points in a Database
Looping over thousands of rows in PHP and calculating the distance for each one is inefficient. For larger datasets, push the work to the database and use a bounding-box pre-filter so that indexes can eliminate most rows before the exact distance check.
<?php
declare(strict_types=1);
function findLocationsWithinRadius(PDO $pdo, float $lat, float $lon, float $radiusKm): array
{
// Approximate bounding box around the circle (degrees).
$angularRadius = $radiusKm / EARTH_RADIUS_KM;
$latDelta = rad2deg($angularRadius);
$lonDelta = rad2deg($angularRadius / max(cos(deg2rad($lat)), 1e-12));
$sql = <<<SQL
SELECT id, name, latitude, longitude
FROM locations
WHERE latitude BETWEEN :minLat AND :maxLat
AND longitude BETWEEN :minLon AND :maxLon
AND ST_Distance_Sphere(POINT(longitude, latitude), POINT(:lon, :lat)) <= :radiusMeters
SQL;
$stmt = $pdo->prepare($sql);
$stmt->execute([
':minLat' => $lat - $latDelta,
':maxLat' => $lat + $latDelta,
':minLon' => $lon - $lonDelta,
':maxLon' => $lon + $lonDelta,
':lon' => $lon,
':lat' => $lat,
':radiusMeters' => $radiusKm * 1000,
]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}Key points about this approach:
It uses prepared statements, so coordinates are never concatenated into SQL.
A composite index on
(latitude, longitude)allows the bounding-box condition to narrow the search quickly.ST_Distance_Sphereis available in MySQL 5.7+ and MariaDB 10.2.38+. Note the argument order:POINT(longitude, latitude).The simple bounding box does not handle circles that cross the ±180° antimeridian or extend over a pole. If your application operates in those regions, split the longitude range or use a spatial index with a proper geographic SRID.
For PostgreSQL, the PostGIS function
ST_DWithinon ageographycolumn is the recommended equivalent.
Security and Privacy Considerations
Location data is personal data, and geofencing logic is frequently used for access decisions. Keep the following in mind:
Never trust client-supplied coordinates for security decisions. GPS positions from browsers and mobile devices can be spoofed trivially. A radius check is suitable for convenience features (for example, showing nearby stores), but it should not be the sole control for attendance, fraud prevention, or access control.
Validate all inputs. Reject non-numeric, non-finite, and out-of-range values before they reach your business logic or database.
Minimize retention. Store precise coordinates only when necessary, reduce precision where possible (four decimal places is roughly 11 meters), and define a retention period. Regulations such as the GDPR treat precise location as personal data.
Avoid leaking details in errors. Return generic error messages to clients and keep detailed diagnostics in server-side logs.
Frequently Asked Questions
How do I check if a point is within a radius in PHP?
Calculate the great-circle distance between the center and the point (preferably with the Haversine formula), then compare it with the radius. If the distance is less than or equal to the radius, the point is inside the circle.
Should I use the Haversine formula or the spherical law of cosines?
Both give similar results for most distances, but Haversine is numerically more stable for short distances and is the more common choice in production code.
How accurate is the Haversine formula?
It assumes a spherical Earth, so the error relative to the real ellipsoidal shape is up to about 0.5%. For higher accuracy, use Vincenty's formulae or a geodesic library.
Why does my distance calculation return NaN?
Floating-point rounding can push the value passed to acos() slightly outside the range [-1, 1]. Clamp the value before calling acos(), or use the Haversine formula with clamping as shown above.
What Earth radius should I use?
The mean Earth radius of 6,371 km (6,371.0088 km per IUGG) is the standard choice for spherical distance calculations.
Conclusion
Checking whether a coordinate lies within a circle comes down to calculating the great-circle distance and comparing it with the radius. The original snippet is a compact, working solution, and with the Haversine formula, strict typing, input validation, and a database-side pre-filter, the same idea becomes reliable enough for production geofencing features.
The original code snippet in this post was published by me on Stack Overflow and is licensed under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/).
Questions and answers
How do I check if a point is within a radius in PHP?
Calculate the great-circle distance between the center and the point (preferably with the Haversine formula), then compare it with the radius. If the distance is less than or equal to the radius, the point is inside the circle.
Should I use the Haversine formula or the spherical law of cosines?
Both give similar results for most distances, but Haversine is numerically more stable for short distances and is the more common choice in production code.
How accurate is the Haversine formula?
It assumes a spherical Earth, so the error relative to the real ellipsoidal shape is up to about 0.5%. For higher accuracy, use Vincenty's formulae or a geodesic library.
Why does my distance calculation return NaN?
Floating-point rounding can push the value passed to acos() slightly outside the range [-1, 1]. Clamp the value before calling acos(), or use the Haversine formula with clamping as shown above.
What Earth radius should I use?
The mean Earth radius of 6,371 km (6,371.0088 km per IUGG) is the standard choice for spherical distance calculations.



Discussion