hitocean/distance-calculator
hitocean/distance-calculator is a Laravel/PHP package for calculating distances, likely between geographic coordinates. It offers a simple way to add distance calculations to your application, with a lightweight API for common distance needs.
Install with Composer (the package is a Laravel service provider, so auto-discovery handles registration on Laravel 5.5+):
composer require hitocean/distance-calculator
If a config file is shipped, publish it immediately and read it before writing any code:
php artisan vendor:publish --provider="Hitocean\DistanceCalculator\DistanceCalculatorServiceProvider"
Then open config/distance-calculator.php and check two things: the default unit (km vs. miles) and whether it is configured to call out to an external routing API. Those two settings determine whether you are doing a pure in-process calculation or a network call.
Where to look first: the src/ directory. Packages of this shape are usually a single calculator class plus a facade. Read the calculator class (often DistanceCalculator.php or Calculator.php) end-to-end — it is almost certainly under 200 lines and is the entire public API. Do not assume the facade is bound to an interface; if it is not, you cannot swap it later without touching call sites.
First use case: distance between two coordinate pairs, typically for a delivery-radius or store-locator check:
use Hitocean\DistanceCalculator\Facades\Distance;
$km = Distance::between(
$userLat, $userLng,
$storeLat, $storeLng
);
if ($km <= 15) {
// eligible for same-day delivery
}
Verify against a known pair before trusting it in a feature — London (−0.1276, 51.5072) to Paris (2.3522, 48.8566) is ~343 km. If you get ~213 km, the package is returning miles; if you get something wildly wrong, your argument order is flipped (see Gotchas).
Wrap the facade behind your own service. This is the single most important pattern for this package because it is archived and unmaintained. Define an interface and a thin adapter so the dependency can be replaced without a codebase-wide search-and-replace:
// app/Contracts/DistanceCalculator.php
interface DistanceCalculator { public function kilometers(Point $a, Point $b): float; }
// app/Services/PackageDistanceCalculator.php
final class PackageDistanceCalculator implements DistanceCalculator
{
public function kilometers(Point $a, Point $b): float
{
return Distance::between($a->lat, $a->lng, $b->lat, $b->lng);
}
}
Bind it in a service provider. If the package is abandoned, you reimplement the interface with a plain Haversine function and nothing else changes.
Store-locator pattern (bounding box, then exact distance). Never run the calculator across every row in the table. Filter candidates with a cheap SQL box first, then compute exact distance only on the survivors:
$latDelta = $radiusKm / 111.0;
$lngDelta = $radiusKm / (111.0 * cos(deg2rad($userLat)));
$candidates = Store::whereBetween('lat', [$userLat - $latDelta, $userLat + $latDelta])
->whereBetween('lng', [$userLng - $lngDelta, $userLng + $lngDelta])
->get();
$nearby = $candidates
->map(fn ($s) => ['store' => $s, 'km' => $calculator->kilometers($userPoint, $s->point())])
->filter(fn ($r) => $r['km'] <= $radiusKm)
->sortBy('km')
->values();
This is where the package earns its keep as a pure PHP implementation — no per-row API call.
Distance matrix for routing/queue optimisation. For small N (under ~50), build a matrix and hand it to a TSP/sorting step:
$matrix = [];
foreach ($stops as $i => $a) {
foreach ($stops as $j => $b) {
$matrix[$i][$j] = $calculator->kilometers($a, $b);
}
}
Cache the matrix keyed by the stop set — it is deterministic and expensive to recompute.
Attribute-style access on models. Expose distance as an accessor rather than sprinkling calculator calls through controllers:
public function distanceFrom(Point $origin): float
{
return app(DistanceCalculator::class)->kilometers($origin, $this->point());
}
Integration with geocoding. This package almost certainly does not geocode. Pair it with a geocoder to turn addresses into coordinates, then feed those coordinates in. Keep the two concerns separate so a geocoder swap does not affect distance logic.
It is archived and scores 0.1 for a reason. Last release was 2022-05-12, zero stars, zero dependents. Treat it as a snippet you happen to install, not a maintained dependency. Run composer audit and expect no meaningful security response if something is flagged. Have a deletion plan.
Lat/lng argument order is the classic failure. There is no convention in the ecosystem — some packages take (lat1, lng1, lat2, lng2), others take (lng1, lat1, lng2, lat2) because GeoJSON uses lng-first. A silent swap produces plausible-looking but wrong distances instead of an exception. Sanity-check with a known city pair on day one, and assert the order in a test.
Unit ambiguity. Check whether the return value is kilometres, miles, or metres, and whether the unit is configurable at runtime or only via config. Mixing this up in a radius filter is a production bug that only shows up at the edges of the radius. Pin the unit in a test with a known distance.
Earth radius assumptions. Pure-PHP distance packages use the Haversine formula with a fixed mean Earth radius (6371 km), which treats the Earth as a sphere. Expect errors up to ~0.5% versus a proper geodesic calculation — fine for a 15 km delivery check, not fine for aviation or billing by the metre. If precision matters, the package is the wrong tool regardless of its state.
No database-level support. Everything is computed in PHP after rows are fetched. On large tables the bounding-box pre-filter above is mandatory, not optional. Also note that cosine-scaled longitude deltas break down near the poles; guard against lat within ~a fraction of a degree of ±90.
Degenerate and antipodal inputs. Identical points should return 0.0, and antipodal points are the worst case for Haversine numerical stability. Write tests for both — small math packages frequently skip them.
Config quirks. If the config file references an API key or base URL for a routing provider, an unset key may fail silently (returning straight-line distance) or throw deep inside a Guzzle call. Log the resolved config on boot in staging so you know which mode you are in.
Extension points are likely absent. With zero dependents and no maintenance, do not expect interfaces, events, or a driver manager. The realistic extension strategy is to fork the single calculator class into app/Support/ and drop the Composer dependency — it is a handful of lines of trigonometry, and owning it removes the abandoned-dependency risk entirely. For most teams this is the recommended path from the start; use the package only if you need something working today and will migrate later.
How can I help you explore Laravel packages today?