How to query distances between two coordinates with Eloquent

前端 未结 1 938
孤街浪徒
孤街浪徒 2021-02-09 17:50

I know this has been ask many times. But I didn\'t figure out to make it according to my needs.

I need to query the nearest users from another user. Basically, I have a

相关标签:
1条回答
  • 2021-02-09 17:57

    I found the solution, thanks to EddyTheDove and Ohgodwhy.

    So this is it:

    \App\Model\User::whereNotIn('id', $ids)
               ->where('status', 1)
               ->whereHas('user_location', function($q) use ($radius, $coordinates) { 
                      $q->isWithinMaxDistance($coordinates, $radius);
             })->select('id', 'firstname')
               ->get();
    

    And in my UserLocation Model I have this local scope

    public function scopeIsWithinMaxDistance($query, $coordinates, $radius = 5) {
    
        $haversine = "(6371 * acos(cos(radians(" . $coordinates['latitude'] . ")) 
                        * cos(radians(`latitude`)) 
                        * cos(radians(`longitude`) 
                        - radians(" . $coordinates['longitude'] . ")) 
                        + sin(radians(" . $coordinates['latitude'] . ")) 
                        * sin(radians(`latitude`))))";
    
        return $query->select('id', 'users_id', 'cities_id')
                     ->selectRaw("{$haversine} AS distance")
                     ->whereRaw("{$haversine} < ?", [$radius]);
    }
    

    The original answer by Ohgodwhy is here: Haversine distance calculation between two points in Laravel

    EDIT

    Another way to perform it with stored functions in MySQL:

        DELIMITER $$
    DROP FUNCTION IF EXISTS haversine$$
    
    CREATE FUNCTION haversine(
            lat1 FLOAT, lon1 FLOAT,
            lat2 FLOAT, lon2 FLOAT
         ) RETURNS FLOAT
        NO SQL DETERMINISTIC
        COMMENT 'Returns the distance in degrees on the Earth
                 between two known points of latitude and longitude'
    BEGIN
        RETURN DEGREES(ACOS(
                  COS(RADIANS(lat1)) *
                  COS(RADIANS(lat2)) *
                  COS(RADIANS(lon2) - RADIANS(lon1)) +
                  SIN(RADIANS(lat1)) * SIN(RADIANS(lat2))
                ));
    END$$
    
    DELIMITER ;
    

    I multiply by 111.045 to convert the result in km. (I'm not sure that this value is right, I found many others values not far from this one so if someone have precision about it, it could be nice)

    Original article: https://www.plumislandmedia.net/mysql/stored-function-haversine-distance-computation/

    Then using eloquent:

    \App\Model\User::whereNotIn('id', $ids)
           ->where('status', 1)
           ->whereHas('user_location', function($q) use ($radius, $coordinates) { 
                $q->whereRaw("111.045*haversine(latitude, longitude, '{$coordinates['latitude']}', '{$coordinates['longitude']}') <= " . $radius]);
         })->select('id', 'firstname')
           ->get();
    
    0 讨论(0)
提交回复
热议问题