直接在SQLite中计算两点之间的距离

时间:2012-04-12 15:08:49

标签: android sqlite math

在我的网络/ MySQL应用程序中,我有类似的东西来获得两点之间的距离:

6371 * acos(cos(radians(-19.83996)) * cos(radians(lat)) * cos(radians(-43.94910) - radians(lng)) + sin(radians(-19.83996)) * sin(radians(lat)))

但我在SQLite中测试过,这些数学函数(acos,cos,radians,sin)不存在。有没有东西可以直接在数据库中计算距离?

但是,我有一个使用this method计算的iPhone应用程序。工作得很好,但现在我需要在Android应用程序的数据库中执行相同的搜索。

提前致谢。

更新

我有9000个点来计算距离并获得给定点附近的5个位置。

5 个答案:

答案 0 :(得分:6)

以下是我要做的事情:

按照你的观点。测量a(这是一个要精炼的任意值)~2km宽的方形围绕它并取东/西/北/南边界的值。

查询此方块内的元素。这个很简单,你只需要

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

计算结果。结果不够(显然不到5个,但我要说两次才能确定),以更大的半径重试。太多(比如超过100),再次尝试半径越小。

一旦你有足够的,加载它们,确保你需要的所有5个元素不仅在Xkm宽的方形中,而且在Xkm半径圆中(以避免前一个近似未检测到的潜在的更近的元素)。

另一种方法

仅当您的指定点与您正在搜索的点相对接近时才有效。

测量平坦地球的局部近似。接近你的观点,你可以考虑lat,lon和distance之间的线性关系。这允许您通过简单的微积分进行排序。 (乘法和加法)。再次,选择多一点以便在SQLite请求之后进行正确的计算。

答案 1 :(得分:3)

将cos_lat_rad,sin_lat_rad,cos_lon_rad,sin_lon_rad插入您的表格

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)));

度到弧度

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

查询,以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)));

查询

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;

}

将distance_acos转换为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());

答案 2 :(得分:0)

Angel的回复并没有解决SQLite的问题,因为它仍然包含SQLite中不存在的ACOS功能

经过深入研究后我得出的结论是,应该选择粗略估计在SQLite中使用以下公式:

km的距离估计:

SQRT(SQUARE((TO_LAT-FROM_LAT)*110)+
     SQUARE((TO_LONG-FROM_LONG)*COS(TO_LAT)*111)) 

答案 3 :(得分:0)

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

答案 4 :(得分:-3)

在android中我们有一个Location类,我们需要初始化location类,在其中我们有一个名为distanceBetween()的方法,它给出了两个地理位置之间的距离。

请参阅此LINK