Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculate coordinate distance in Laravel

Tags:

I have a scope in my model which creates an alias and I need to execute a where on it, I understand that MySql dose not allow this.

Standard SQL doesn't allow you to refer to a column alias in a WHERE clause. This restriction is imposed because when the WHERE code is executed, the column value may not yet be determined.

However, I'm wondering if there is a possible laravel work around? My scope creates an alias distance and I want to check the distance.

public static function scopeDistance($query, $lat, $long)
{
    return $query->select(array('*',\DB::raw('( 3959 * acos( cos( radians(' . $lat . ') ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians(' . $long . ') ) + sin( radians(' . $lat .') ) * sin( radians(latitude) ) ) ) AS distance')));
} 

In my controller:

\App\Profile::distance($latitude, $longitude)->where('distance', '<', 50);
like image 1000
user2834482 Avatar asked May 31 '17 19:05

user2834482


People also ask

How do you find the distance between two coordinates in php?

php 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 = $ ...

How do you find the distance between two latitude and longitude in SQL query?

Try the below code: SELECT latitude, longitude, SQRT( POW(69.1 * (latitude – [startlat]), 2) + POW(69.1 * ([startlng] – longitude) * COS(latitude / 57.3), 2)) AS distance.


1 Answers

Firstly, don't forget to remove the static keyword from the function call, Laravel deals with magical static-looking Profile::distance call.

Below are 2 different options to solve the same issue, in either case you will call \App\Profile::distance($latitude, $longitude, 50) from your controller.

Option 1

You can do the calculation in memory instead of dealing with subqueries.

public function scopeDistance($query, $lat, $long, $distance) {
    return Profile::all()->filter(function ($profile) use ($lat, $long, $distance) {
        $actual = 3959 * acos(
            cos(deg2rad($lat)) * cos(deg2rad($profile->latitude))
            * cos(deg2rad($profile->longitude) - deg2rad($long))
            + sin(deg2rad($lat)) * sin(deg2rad($profile->latitude))
        );
        return $distance < $actual;
    });
}

Option 2

You can execute a SQL subquery, make sure to end the call with get():

public function scopeDistance($query, $lat, $long, $distance) {
    return $query->having('distance', '<', $distance)
             ->select(DB::raw("*,
                     (3959 * ACOS(COS(RADIANS($lat))
                           * COS(RADIANS(latitude))
                           * COS(RADIANS($long) - RADIANS(longitude))
                           + SIN(RADIANS($lat))
                           * SIN(RADIANS(latitude)))) AS distance")
             )->orderBy('distance', 'asc')
              ->get();
}
like image 81
Dov Benyomin Sohacheski Avatar answered Sep 21 '22 13:09

Dov Benyomin Sohacheski