代码之家  ›  专栏  ›  技术社区  ›  Pentium10

在SQLite中查询以获取基于Radius的记录?

  •  26
  • Pentium10  · 技术社区  · 14 年前

    我有一个在MySQL中运行良好的查询

    SELECT ((ACOS(SIN(12.345 * PI() / 180) * SIN(lat * PI() / 180) +
             COS(12.345 * PI() / 180) * COS(lat * PI() / 180) * COS((67.89 - lon) * 
             PI() / 180)) * 180 / PI()) * 60 * 1.1515 * 1.609344) AS distance, poi.* 
    FROM poi
    WHERE lang='eng' 
    HAVING distance<='30'
    

    距离以公里为单位,输入为 lat=12.345 lon=67.89

    SQLite是3,我不能用它运行自定义函数,因为它在Android上。我也没有acos()等。。。因为这不是标准SQLite的一部分。

    3 回复  |  直到 14 年前
        1
  •  15
  •   Community CDub    7 年前

    您可以创建4个新列,作为 lat lon . 自 cos(a+b) = cos a cos b - sin a sin b SIN(12.345 * PI() / 180) 可以在程序运行前计算查询,把大的“距离”表达式缩减成某种形式 P * SIN_LAT + Q * COS_LAT + ... 可以由SQLite3处理的。

    顺便说一句,另见 Sqlite on Android: How to create a sqlite dist db function - to be used in the app for distance calculation using lat, long

        2
  •  31
  •   EricLarch    13 年前

    这个想法来源于KennyTM(参见公认的回答),它意味着在表中添加4列来存储纬度和经度的正弦值和余弦值。

    public static void injectLocationValues(ContentValues values, double latitude, double longitude) {
        values.put(LocationColumns.LATITUDE, latitude);
        values.put(LocationColumns.LONGITUDE, longitude);
        values.put(LocationColumns.COSLAT, Math.cos(MathUtil.deg2rad(latitude)));
        values.put(LocationColumns.SINLAT, Math.sin(MathUtil.deg2rad(latitude)));
        values.put(LocationColumns.COSLNG, Math.cos(MathUtil.deg2rad(longitude)));
        values.put(LocationColumns.SINLNG, Math.sin(MathUtil.deg2rad(longitude)));
    }
    
    public static double deg2rad(double deg) {
        return (deg * Math.PI / 180.0);
    }
    

    然后可以使用以下函数构建投影:

    /**
     * Build query based on distance using spherical law of cosinus
     * 
     * d = acos(sin(lat1).sin(lat2)+cos(lat1).cos(lat2).cos(long2−long1)).R
     * where R=6371 and latitudes and longitudes expressed in radians
     * 
     * In Sqlite we do not have access to acos() sin() and lat() functions.
     * Knowing that cos(A-B) = cos(A).cos(B) + sin(A).sin(B)
     * We can determine a distance stub as:
     * d = sin(lat1).sin(lat2)+cos(lat1).cos(lat2).(cos(long2).cos(long1)+sin(long2).sin(long1))
     * 
     * First comparison point being fixed, sin(lat1) cos(lat1) sin(long1) and cos(long1)
     * can be replaced by constants.
     * 
     * Location aware table must therefore have the following columns to build the equation:
     * sinlat => sin(radians(lat))
     * coslat => cos(radians(lat))
     * coslng => cos(radians(lng))
     * sinlng => sin(radians(lng))
     *  
     * Function will return a real between -1 and 1 which can be used to order the query.
     * Distance in km is after expressed from R.acos(result) 
     *  
     * @param latitude, latitude of search
     * @param longitude, longitude of search
     * @return selection query to compute the distance
     */
    public static String buildDistanceQuery(double latitude, double longitude) {
        final double coslat = Math.cos(MathUtil.deg2rad(latitude));
        final double sinlat = Math.sin(MathUtil.deg2rad(latitude));
        final double coslng = Math.cos(MathUtil.deg2rad(longitude));
        final double sinlng = Math.sin(MathUtil.deg2rad(longitude));
        //@formatter:off
        return "(" + coslat + "*" + LocationColumns.COSLAT
                + "*(" + LocationColumns.COSLNG + "*" + coslng
                + "+" + LocationColumns.SINLNG + "*" + sinlng
                + ")+" + sinlat + "*" + LocationColumns.SINLAT 
                + ")";
        //@formatter:on
    }
    

    public static double convertPartialDistanceToKm(double result) {
        return Math.acos(result) * 6371;
    }
    

    如果要使用部分距离对查询进行排序,则需要排序DESC而不是ASC。

        3
  •  15
  •   sclaeys    10 年前

    1. 为存储在数据库中的每个项目预先计算这些值(并存储它们):

      cos_lat = cos(lat * PI / 180)
      sin_lat = sin(lat * PI / 180)
      cos_lng = cos(lng * PI / 180)
      sin_lng = sin(lng * PI / 180)
      
    2. CUR_cos_lat = cos(cur_lat * PI / 180)
      CUR_sin_lat = sin(cur_lat * PI / 180)
      CUR_cos_lng = cos(cur_lng * PI / 180)
      CUR_sin_lng = sin(cur_lng * PI / 180)
      cos_allowed_distance = cos(2.0 / 6371) # This is 2km
      
    3. 您的SQL查询将如下所示(用刚才计算的值替换CUR\ux*)