Fastest Way to Find Distance Between Two Lat/Long Points

后端 未结 15 945
时光说笑
时光说笑 2020-11-21 10:12

I currently have just under a million locations in a mysql database all with longitude and latitude information.

I am trying to find the distance between one point a

相关标签:
15条回答
  • 2020-11-21 10:34

    Here is a very detailed description of Geo Distance Search with MySQL a solution based on implementation of Haversine Formula to mysql. The complete solution description with theory, implementation and further performance optimization. Although the spatial optimization part didn't work correct in my case. http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL

    0 讨论(0)
  • 2020-11-21 10:35

    A fast, simple and accurate (for smaller distances) approximation can be done with a spherical projection. At least in my routing algorithm I get a 20% boost compared to the correct calculation. In Java code it looks like:

    public double approxDistKm(double fromLat, double fromLon, double toLat, double toLon) {
        double dLat = Math.toRadians(toLat - fromLat);
        double dLon = Math.toRadians(toLon - fromLon);
        double tmp = Math.cos(Math.toRadians((fromLat + toLat) / 2)) * dLon;
        double d = dLat * dLat + tmp * tmp;
        return R * Math.sqrt(d);
    }
    

    Not sure about MySQL (sorry!).

    Be sure you know about the limitation (the third param of assertEquals means the accuracy in kilometers):

        float lat = 24.235f;
        float lon = 47.234f;
        CalcDistance dist = new CalcDistance();
        double res = 15.051;
        assertEquals(res, dist.calcDistKm(lat, lon, lat - 0.1, lon + 0.1), 1e-3);
        assertEquals(res, dist.approxDistKm(lat, lon, lat - 0.1, lon + 0.1), 1e-3);
    
        res = 150.748;
        assertEquals(res, dist.calcDistKm(lat, lon, lat - 1, lon + 1), 1e-3);
        assertEquals(res, dist.approxDistKm(lat, lon, lat - 1, lon + 1), 1e-2);
    
        res = 1527.919;
        assertEquals(res, dist.calcDistKm(lat, lon, lat - 10, lon + 10), 1e-3);
        assertEquals(res, dist.approxDistKm(lat, lon, lat - 10, lon + 10), 10);
    
    0 讨论(0)
  • 2020-11-21 10:39

    A MySQL function which returns the number of metres between the two coordinates:

    CREATE FUNCTION DISTANCE_BETWEEN (lat1 DOUBLE, lon1 DOUBLE, lat2 DOUBLE, lon2 DOUBLE)
    RETURNS DOUBLE DETERMINISTIC
    RETURN ACOS( SIN(lat1*PI()/180)*SIN(lat2*PI()/180) + COS(lat1*PI()/180)*COS(lat2*PI()/180)*COS(lon2*PI()/180-lon1*PI()/180) ) * 6371000
    

    To return the value in a different format, replace the 6371000 in the function with the radius of Earth in your choice of unit. For example, kilometres would be 6371 and miles would be 3959.

    To use the function, just call it as you would any other function in MySQL. For example, if you had a table city, you could find the distance between every city to every other city:

    SELECT
        `city1`.`name`,
        `city2`.`name`,
        ROUND(DISTANCE_BETWEEN(`city1`.`latitude`, `city1`.`longitude`, `city2`.`latitude`, `city2`.`longitude`)) AS `distance`
    FROM
        `city` AS `city1`
    JOIN
        `city` AS `city2`
    
    0 讨论(0)
  • 2020-11-21 10:44

    The full code with details about how to install as MySQL plugin are here: https://github.com/lucasepe/lib_mysqludf_haversine

    I posted this last year as comment. Since kindly @TylerCollier suggested me to post as answer, here it is.

    Another way is to write a custom UDF function that returns the haversine distance from two points. This function can take in input:

    lat1 (real), lng1 (real), lat2 (real), lng2 (real), type (string - optinal - 'km', 'ft', 'mi')
    

    So we can write something like this:

    SELECT id, name FROM MY_PLACES WHERE haversine_distance(lat1, lng1, lat2, lng2) < 40;
    

    to fetch all records with a distance less then 40 kilometers. Or:

    SELECT id, name FROM MY_PLACES WHERE haversine_distance(lat1, lng1, lat2, lng2, 'ft') < 25;
    

    to fetch all records with a distance less then 25 feet.

    The core function is:

    double
    haversine_distance( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error ) {
        double result = *(double*) initid->ptr;
        /*Earth Radius in Kilometers.*/ 
        double R = 6372.797560856;
        double DEG_TO_RAD = M_PI/180.0;
        double RAD_TO_DEG = 180.0/M_PI;
        double lat1 = *(double*) args->args[0];
        double lon1 = *(double*) args->args[1];
        double lat2 = *(double*) args->args[2];
        double lon2 = *(double*) args->args[3];
        double dlon = (lon2 - lon1) * DEG_TO_RAD;
        double dlat = (lat2 - lat1) * DEG_TO_RAD;
        double a = pow(sin(dlat * 0.5),2) + 
            cos(lat1*DEG_TO_RAD) * cos(lat2*DEG_TO_RAD) * pow(sin(dlon * 0.5),2);
        double c = 2.0 * atan2(sqrt(a), sqrt(1-a));
        result = ( R * c );
        /*
         * If we have a 5th distance type argument...
         */
        if (args->arg_count == 5) {
            str_to_lowercase(args->args[4]);
            if (strcmp(args->args[4], "ft") == 0) result *= 3280.8399;
            if (strcmp(args->args[4], "mi") == 0) result *= 0.621371192;
        }
    
        return result;
    }
    
    0 讨论(0)
  • 2020-11-21 10:45
    $objectQuery = "SELECT table_master.*, ((acos(sin((" . $latitude . "*pi()/180)) * sin((`latitude`*pi()/180))+cos((" . $latitude . "*pi()/180)) * cos((`latitude`*pi()/180)) * cos(((" . $longitude . "- `longtude`)* pi()/180))))*180/pi())*60*1.1515  as distance FROM `table_post_broadcasts` JOIN table_master ON table_post_broadcasts.master_id = table_master.id WHERE table_master.type_of_post ='type' HAVING distance <='" . $Radius . "' ORDER BY distance asc";
    
    0 讨论(0)
  • 2020-11-21 10:46

    Using mysql

    SET @orig_lon = 1.027125;
    SET @dest_lon = 1.027125;
    
    SET @orig_lat = 2.398441;
    SET @dest_lat = 2.398441;
    
    SET @kmormiles = 6371;-- for distance in miles set to : 3956
    
    SELECT @kmormiles * ACOS(LEAST(COS(RADIANS(@orig_lat)) * 
     COS(RADIANS(@dest_lat)) * COS(RADIANS(@orig_lon - @dest_lon)) + 
     SIN(RADIANS(@orig_lat)) * SIN(RADIANS(@dest_lat)),1.0)) as distance;
    

    See: https://andrew.hedges.name/experiments/haversine/

    See: https://stackoverflow.com/a/24372831/5155484

    See: http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/

    NOTE: LEAST is used to avoid null values as a comment suggested on https://stackoverflow.com/a/24372831/5155484

    0 讨论(0)
提交回复
热议问题