如何从 SQLite 数据库中的用户位置检索特定范围内的一组位置

Posted

技术标签:

【中文标题】如何从 SQLite 数据库中的用户位置检索特定范围内的一组位置【英文标题】:How to retrieve a set of locations within particular range from user's location from SQLite database 【发布时间】:2015-11-03 06:12:32 【问题描述】:

我的SQLite 数据库表中存储了一些位置坐标。我想检索距用户当前位置 1 公里范围内的位置。现在我正在从数据库中获取所有值,并编写了一个方法来检索我范围内的值。这给我留下了很大的开销,因为我的表可能包含 1000 多个坐标。

所以我正在寻找一种更好的方法来做到这一点。是否可以直接使用 SELECT 查询直接检索我范围内的位置? 我找到了question 1 和question 2,但找不到可能的解决方法。任何帮助将不胜感激。

这是我现在的 SELECT 查询:

String selectQuery = "SELECT  "+COLUMN_OBJECTID+","+COLUMN_OBJECTNAME_ENGLISH+"," 
                      +COLUMN_OBJECTNAME_ARABIC+","+COLUMN_OBJECTLATITUDE+","+COLUMN_OBJECTLONGITUDE+"," 
                              +COLUMN_OBJECTCATEGORYID+","+COLUMN_OBJECTADDRESS_ENGLISH+","+COLUMN_OBJECTADDRESS_ARABIC 
                              +" FROM " + TABLE_OBJECTLIST+" WHERE "+COLUMN_OBJECTCATEGORYID+"='"+categoryId+"' AND "+ 
                              ((userLat-Double.parseDouble(COLUMN_OBJECTLATITUDE))*(userLat-Double.parseDouble(COLUMN_OBJECTLATITUDE)) 
                                      +(userLong-Double.parseDouble(COLUMN_OBJECTLONGITUDE))*(userLong-Double.parseDouble(COLUMN_OBJECTLONGITUDE))<=range);

【问题讨论】:

您的问题是您混淆了 SQL 和 Java。您的 COLUMN_* 符号仅适用于 SQL,parseDouble() 仅适用于 Java。你应该首先构造正确的 SQL 查询(在 SQLite Manager 之类的工具中),然后弄清楚如何从 Java 中执行它。 好的。那么你能帮我用我需要的条件构造这个查询吗? 您链接到的问题的答案已经显示了正确的 SQL。 我将我的 lat long 值保存为字符串。所以如果我把它改成双倍,我可以在我的查询中使用它吗? @Jas 你混合了 Java 函数和 SQL 代码!只需编写简单的 SQL 代码.. 看我的例子 【参考方案1】:

您可以使用以下 where 条件编写 SQL 查询:

where ( (my_lat - LAT)*(my_lat - LAT) + (my_lon - LON)*(my_lon - LON) ) <= 1KM

这个想法是使用毕达哥拉斯方法来计算近似位置并在此基础上进行过滤。 这里我没有取平方根,因为我猜 SQLite 中的 SQL 函数没有 sqrt。

这对近似计算很有用...

我使用了以下 SQL 并且它工作正常......

// Table with columns as String and Float
CREATE TABLE "locations" ("id" INTEGER PRIMARY KEY  AUTOINCREMENT  NOT NULL , "lat_string" VARCHAR, "long_string" VARCHAR, "lat_val" FLOAT, "long_val" FLOAT)

// Insert example data
INSERT INTO "locations" VALUES(1,'12.9587926','77.7477416',12.9587926,77.7477416);
INSERT INTO "locations" VALUES(2,'12.9973486','77.6967362',12.9973486,77.69673619999999);
INSERT INTO "locations" VALUES(3,'12.9715987','77.5945627',12.9715987,77.5945627);
INSERT INTO "locations" VALUES(4,'12.9629354','77.7122996',12.9629354,77.7122996);

// Select when column format is string. This works in SQLIte
SELECT id, ( (77.7580827 - long_string)*(77.7580827 - long_string) + (12.9905542 - lat_string)*(12.9905542 - lat_string) ) as dist FROM locations

// Select when column format is float. This works in SQLIte
SELECT id, ( (77.7580827 - long_val)*(77.7580827 - long_val) + (12.9905542 - lat_val)*(12.9905542 - lat_val) ) as dist FROM locations

【讨论】:

什么是纬度和经度?这些是哪些经纬度值? LAT 和 LON 是您的 SQLite DB 列名,您在其中查找地理位置... my_lat 和 my_lon 是您的参考点的地理位置 好的。感谢您的回答。让我试试这个 我试过了,但是如果不使用游标,我无法在 SELECT 查询中从数据库中检索值 它给出 NumberFormatException【参考方案2】:

这里有一些代码可以让你知道如何让它工作:

public static double[] getNeighbourhoodArea(
            final double lat, final double lng, final int distInMtrs) 

        double[] area = new double[4];

        final double latRadian = Math.toRadians(lat);

        final double degLatKm = 110.574235;
        final double degLngKm = 110.572833 * Math.cos(latRadian);
        final double deltaLat = distInMtrs / 1000.0 / degLatKm;
        final double deltaLong = distInMtrs / 1000.0 / degLngKm;

        final double minLat = lat - deltaLat;
        final double minLng = lng - deltaLong;
        final double maxLat = lat + deltaLat;
        final double maxLng = lng + deltaLong;

        area[0] = minLat;
        area[1] = minLng;
        area[2] = maxLat;
        area[3] = maxLng;

        return area;
    


    /**
     * search POIs in the neighbourhood
     */
    private PntInrtst collectPOIs(double lat, double lng) 

        if (mDb == null) return Const.NULL_POI;

        Cursor cursorStat = mDb.getPoisInArea(lat, lng, Const.SIDE_LENGTH_GEO_OFFSET);

        double area[] = Logic.getProtectionArea(lat, lng, Const.SIDE_LENGTH_GEO_OFFSET);

        ArrayList<PntInrtst> poiArray = new ArrayList<PntInrtst>();

        PntInrtst poi = Const.NULL_POI;

        if (cursorStat.moveToFirst()) 
            for (int i = 0; i < cursorStat.getCount(); i++) 
                double potLat = cursorStat.getFloat(Const.COL_LA);
                double potLng = cursorStat.getFloat(Const.COL_LO);

                if ((potLat < area[Const.MAX_LAT] && potLat > area[Const.MIN_LAT])
                    && (potLng < area[Const.MAX_LNG] && potLng > area[Const.MIN_LNG])) 

                    poi = Logic.getPoiByCursor(getApplicationContext(), cursorStat);
                    poiArray.add(poi);

                
                cursorStat.moveToNext();
             // End "Cursor"
        
        cursorStat.close();

        // more than once, fire the nearest
        if (poiArray.size() > 1) return closest(poiArray, lat, lng);
        else return poi; // one or null
    


    /**
     * filter POIs which won't be useful (avoids flooding the cache)
     */
    public Cursor getPoisInArea(double latitude, double longitude, int range) 

        double area[] = getNeighbourhoodArea(latitude, longitude, range);

        String where = "la" + "<" + area[MAX_LAT] +
                       " AND " + "la" + ">" + area[MIN_LAT] +
                       " AND " + "lo" + "<" + area[MAX_LNG] +
                       " AND " + "lo" + ">" + area[MIN_LNG];

        SQLiteDatabase db = mDbHelper.getReadableDatabase();
        return db.query(Const.POI_DB_TABLE, null, where, null, null, null, null);
    

【讨论】:

谢谢。让我试试这个 嘿。我在 Logic、PntInrtst、Const 等地方遇到错误,因为无法解决 你能告诉我PntInrtst 数据类型是什么吗? 那是我的“PointOfInterest”——一个地理位置,包含一组图元。此时您可以从数据库中获取对象。如果你有例如数据源中的加油站,然后返回它们。 :) @Martin Pfeffer。你能给我这个主题的示例链接吗?(完整示例)。非常感谢。

以上是关于如何从 SQLite 数据库中的用户位置检索特定范围内的一组位置的主要内容,如果未能解决你的问题,请参考以下文章

如何从sqlite中的多个表中选择特定列?

从 SQLite 数据库中检索特定项目

如何使用 Swift 从 CoreData sqlite 表中获取特定的行数据

如何从android中的sqlite中检索数据

从 Firebase 为每个特定帖子检索特定数据的比 datasnapshot 更好的方法?返回的位置错误

如何从 SQLite 表中检索最后一个自动增量 ID?