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

时间:2015-11-03 06:12:32

标签: android sqlite geolocation

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

所以我正在寻找一种更好的方法来做到这一点。是否可以直接使用SELECT查询直接检索我的范围内的位置? 我找到了question 1question 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);

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

以下是一些代码,可以让您了解如何使其正常工作:

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