Calculate distance between two points directly in SQLite

前端 未结 5 622
北海茫月
北海茫月 2020-12-17 19:15

In my web/MySQL application I have something like this to get distance between two points:

6371 * acos(cos(radians(-19.83996)) * cos(radians(lat)) * cos(radi         


        
相关标签:
5条回答
  • 2020-12-17 19:50
    For windows:
    
    Install minGw full options. modify environment variable: system variable path, including
    
    c:\mingw\bin
    
    
    test functionality command:
    
    g++ --version
    
    copy files: extension-functions.c, sqlite3.h, sqlite3ext.h in sqlite3 program directory. Go to sqlite3 directory and compile:
    
    gcc -shared -I "path" -o libsqlitefunctions.so extension-functions.c
    
       (path = path of sqlite3ext.h; i.e. C:\sqlite3)
    
    If the program is built so that loading extensions is permitted, the following will work:
    
       sqlite> SELECT load_extension('./libsqlitefunctions.so');
    
       sqlite> select cos(radians(45));
    
       0.707106781186548
    
    SQLite Distance implementation:
    
    From:  https://www.movable-type.co.uk/scripts/latlong.html https://en.wikipedia.org/wiki/Haversine_formula
    
    Distance
    This uses the ‘haversine’ formula to calculate the great-circle distance between two points – that is, the shortest distance over the earth’s surface – giving an ‘as-the-crow-flies’ distance between the points (ignoring any hills they fly over, of course!).
    
    Haversine
    formula:    a = sin²(Δφ/2) + cos φ1 * cos φ2 * sin²(Δλ/2)
    c = 2 * atan2( √a, √(1−a) )
    c = 2 * 
    d = R * c
    where   φ is latitude, λ is longitude, R is earth’s radius (mean radius = 6,371km);
    note that angles need to be in radians to pass to trig functions!
    JavaScript: 
    const R = 6378136.6 ; // meters equatorial radius
    const φ1 = lat1 * Math.PI/180; // φ, λ in radians
    const φ2 = lat2 * Math.PI/180;
    const Δφ = (lat2-lat1) * Math.PI/180;
    const Δλ = (lon2-lon1) * Math.PI/180;
    
    const a = Math.sin(Δφ/2) * Math.sin(Δφ/2) +
              Math.cos(φ1) * Math.cos(φ2) *
              Math.sin(Δλ/2) * Math.sin(Δλ/2);
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    
    const c = 2 * Math.asen(MIN (1, Math.sqrt(a))); //sqlite implementation
    
    const d = R * c; // in metres
    
    Distance = 2 * R * ASIN( MIN (1, SQRT( SIN( (RADIANS(lat1)-RADIANS(lat2))/2 )^2 + COS( RADIANS(lat1) )*COS( RADIANS(lat2) )*SIN( (RADIANS(long1)-RADIANS(long2))/2 )^2 )))
    
    Physical Properties of Earth https://en.wikipedia.org/wiki/Earth_ellipsoid :Ecuatorial radius: 6378.1366 Kms. Average radius: 6367 Kms
    
    
    Constant = 2 * 6378136.6 = 12756273.2
    
    SQLite query command with coordinates taken from table PAR: 
    
    ROUND (
          12756273.2 * ASIN(
                            MIN (1 , 
                                    SQRT(
                                        POWER( SIN(RADIANS(PAR.Lat1 - PAR.Lat2)/2) , 2) + 
                                        COS(RADIANS(PAR.Lat1)) * COS(RADIANS(PAR.Lat2)) * POWER ( SIN(RADIANS(PAR.Long1 - PAR.Long2)/2) , 2)
                                          )
                                )
                            )
             , 0) AS Distance
    
    0 讨论(0)
  • 2020-12-17 19:51

    Here is what I would do:

    Take your given point. Measure a (that is an arbitrary value to be refined) ~2km wide square around it and take the values for the east/west/north/south bounds.

    Make a query for elements inside this square. This one is easy, you just have to

    select * from points where lat between ? and ? and lon between ? and ?
    

    Count your result. Not enough result (less than 5, obviously, but I would say twice that to be sure), retry with a larger radius. Too much (say, more than 100), try again with a smaller radius.

    Once you have enough, load them, make sure all 5 elements you need are not only in the Xkm wide square, but also in the Xkm radius circle (to avoid having a potential closer element not detected by the previous approximation).

    Another approach

    Valid only if your given point is relatively close to those you are searching.

    Measure a local approximation of a flat earth. Close to your point, you can consider a linear relation between lat, lon, and distance. That allows you to make a request sorted by a simple calculus. (multiplication and addition). Again, select a little more points in order to make the proper calculation after the SQLite request.

    0 讨论(0)
  • 2020-12-17 19:55

    Insert cos_lat_rad,sin_lat_rad,cos_lon_rad,sin_lon_rad in to your table

    contentValues.put("cos_lat_rad", Math.cos(deg2rad(latitude)));
    contentValues.put("sin_lat_rad", Math.sin(deg2rad(latitude)));
    contentValues.put("cos_lon_rad", Math.cos(deg2rad(longitude)));
    contentValues.put("sin_lon_rad", Math.sin(deg2rad(longitude)));
    

    degree to radian

    public static double deg2rad(double deg) {
        return (deg * Math.PI / 180.0);
    }
    

    query , distance in km

     Cursor c=database.dis(String.valueOf(Math.cos((double) distance / (double) 6380)), Math.cos(deg2rad(latitude)), Math.sin(deg2rad(latitude)), Math.cos(deg2rad(longitude)), Math.sin(deg2rad(longitude)));
    

    query

    public Cursor dis(String dis, double cos_lat_rad, double sin_lat_rad, double cos_lon_rad, double sin_lon_rad) {
    
        Cursor cursor = sqLiteDatabase.rawQuery("SELECT * ,(" + sin_lat_rad + "*\"sin_lat_rad\"+" + cos_lat_rad + "*\"cos_lat_rad\"*(" + sin_lon_rad + "*\"sin_lon_rad\"+" + cos_lon_rad + "*\"cos_lon_rad\")) AS \"distance_acos\" FROM parish WHERE ("+sin_lat_rad+" * \"sin_lat_rad\" +"+ cos_lat_rad +"* \"cos_lat_rad\" * (+"+sin_lon_rad +"* \"sin_lon_rad\" + "+cos_lon_rad +"* \"cos_lon_rad\")) >"+dis+ " ORDER BY \"distance_acos\" DESC ", null);
        return cursor;
    
    }
    

    convert distance_acos to km

     if(c.moveToFirst())
            do {
                double distance_acos= c.getDouble(c.getColumnIndex("distance_acos"));
                         String Distance=String.valueOf(Math.acos(distance_acos) * 6380); 
            }while (c.moveToNext());
    
    0 讨论(0)
  • 2020-12-17 19:56

    In android we have a Location class, we need initialize the location class and in it we have a method called distanceBetween() which gives the distance between two geopoints.

    Refer this LINK

    0 讨论(0)
  • 2020-12-17 19:57

    Reply of Angel does not solve the problem for SQLite since it still includes ACOS function which does not exist in SQLite

    What I have concluded after a thorough research is that one should pick a rough estimation to use in SQLite with the formula below:

    Distance estimation for km:

    SQRT(SQUARE((TO_LAT-FROM_LAT)*110)+
         SQUARE((TO_LONG-FROM_LONG)*COS(TO_LAT)*111)) 
    
    0 讨论(0)
提交回复
热议问题