أريد أن أكون قادرًا على إجراء SQL Select (SQLite) على جدول وترتيب كل مسافة من كل صفوف من نقطة تعسفية

StackOverflow https://stackoverflow.com/questions/4231177

سؤال

لدي قاعدة بيانات SQLite مع جدول مليء بالمواقع الجغرافية ، كل منها مخزّن كقيمة خطوط الطول والخطوط بالدرجات. أردت أن أكون قادرًا على إجراء SQL Select على هذا الجدول وطلب مسافة كل صف من نقطة تعسفية. لقد حققت هذا باستخدام وظيفة SQLite المخصصة المحددة (RISTIONFUNC) أدناه.

إليكم الوظيفة ، إلى جانب ماكرو الراحة للتحويل من الدرجات إلى الراديان. تعتمد هذه الوظيفة على آلة حاسبة المسافة عبر الإنترنت ، والتي تستخدم القانون الكروي لجيب التمام.

http://en.wikipedia.org/wiki/spherical_law_of_cosines

"في علم المثلثات الكروية ، فإن قانون جيب التمام (الذي يُطلق عليه أيضًا قاعدة جيب التمام للجانبين) هو نظرية تتعلق بجوانب المثلثات الكروية ، مماثلة للقانون العادي لجيب التمام من علم المثلثات في الطائرة."

#define DEG2RAD(degrees) (degrees * 0.01745327) // degrees * pi over 180

"المسافة" في (location.m)

static void distanceFunc(sqlite3_context *context, int argc, sqlite3_value **argv) {
 // check that we have four arguments (lat1, lon1, lat2, lon2)
 assert(argc == 4);

 // check that all four arguments are non-null
 if (sqlite3_value_type(argv[0]) == SQLITE_NULL || sqlite3_value_type(argv[1]) == SQLITE_NULL || sqlite3_value_type(argv[2]) == SQLITE_NULL || sqlite3_value_type(argv[3]) == SQLITE_NULL) {
  sqlite3_result_null(context);
  return;
 }

 // get the four argument values
 double lat1 = sqlite3_value_double(argv[0]);
 double lon1 = sqlite3_value_double(argv[1]);
 double lat2 = sqlite3_value_double(argv[2]);
 double lon2 = sqlite3_value_double(argv[3]);

 // convert lat1 and lat2 into radians now, to avoid doing it twice below
 double lat1rad = DEG2RAD(lat1);
 double lat2rad = DEG2RAD(lat2);

 // apply the spherical law of cosines to our latitudes and longitudes, and set the result appropriately
 // 6378.1 is the approximate radius of the earth in kilometres
 sqlite3_result_double(context, acos(sin(lat1rad) * sin(lat2rad) + cos(lat1rad) * cos(lat2rad) * cos(DEG2RAD(lon2) - DEG2RAD(lon1))) * 6378.1);
}

أسمي طريقتي "getDistanceBetweenLonglat" والتي هي أيضًا في "location.m" مثل هذا:

تستخدم في بلدي (appdelegate.m):

Location *location = [[Location alloc] init];
location.latitude = -37.1134; //double property
location.longitude = 145.4254; //double property
[location getDistanceBetweenLongLat:[self dbPath]];

طريقة "getDistanceBetweenlonglat" في (location.m):

- (void) getDistanceBetweenLongLat:(NSString *)dbPath {

    AppDelegate *_ad = (AppDelegate *)[[UIApplication sharedApplication] delegate];

    if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {

        sqlite3_create_function(database, "distance", 4, SQLITE_UTF8, NULL, &distanceFunc, NULL, NULL);

        const char *sql = "select * from locations order by distance(latitude, longitude, ?, ?)";
        sqlite3_stmt *selectstmt;

        sqlite3_bind_double(selectStmt, 1, latitude);
        sqlite3_bind_double(selectStmt, 2, longitude);

        if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {

            while(sqlite3_step(selectstmt) == SQLITE_ROW) {

                NSInteger primaryKey = sqlite3_column_int(selectstmt, 0);
                Location *location = [[Location alloc] initWithPrimaryKey:primaryKey];
                location.latitude = sqlite3_column_double(selectstmt, 1);
                location.longitude = sqlite3_column_double(selectstmt, 2);
                location.title = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 3)];
                location.street1 = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 4)];
                location.street2 = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 5)];
                location.suburb = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 6)];
                NSInteger postCodeItem = sqlite3_column_int(selectstmt, 7);
                location.postcode = postCodeItem;

                location.isDirty = NO;

                [_ad.locations addObject:location];
                [location release];

            }
        }
    } else {
        //Even though the open call failed, close the database connection to release all the memory.
        sqlite3_close(database);
    }
}

هذا يطلق على "RISTINGFUNC" على ما يرام. ومع ذلك ، عندما يصل إلى البيان حيث يتحقق من أن جميع الحجج الأربعة غير خالية من الفرق ، فإنها تعود جميعًا إلى أنها لاغية.

ومع ذلك ، عندما أقوم بتغيير بيان SQL أعلاه إلى ما يلي:

const char *sql = "select * from locations order by distance(latitude, longitude, '?', '?')"; //single quotes added

الحجج الأربع لا تعود كخلفية. ومع ذلك ، فإن قيمتي الوسيطة الأخيرة هما 0 عندما يجب أن تكون التالية ، أليس كذلك؟

location.latitude = -37.1134; //double property
location.longitude = 145.4254; //double property:

من "RISTIONFUNC" في (location.m):

double lat1 = sqlite3_value_double(argv[0]); // -17.7699 from 1st result in Locations table
double lon1 = sqlite3_value_double(argv[1]); // 245.1103 from 1st result in Locations table
double lat2 = sqlite3_value_double(argv[2]); // 0
double lon2 = sqlite3_value_double(argv[3]); // 0

أستخدم نفس الطريقة إلى حد كبير للحصول على البيانات الأولية لعرضها وهذا الصفيف الخاص يملأ بشكل جيد. هذه المرة أريد فقط أن تحتوي مجموعة المواقف الخاصة بي (في _ad.locations) على النتائج المطلوبة بالمسافة بدلاً من ذلك حتى أتمكن من عرضها في UableView.

ملاحظة: يمكن أيضًا العثور على "RISTIONFUNC" المستخدمة في الرابط التالي: http://www.thismuchiknow.co.uk/؟p=71&cpage=1#comment-30834

هل كانت مفيدة؟

المحلول

أستخدم تباينًا ، حيث يكون Latval و Longval موقعي الحالي:

NSString *sqlString = @"SELECT * FROM stores WHERE status = 1 AND distance(latitude, longitude, %f, %f, id) < 800";
sqlString = [NSString stringWithFormat:sqlString, latVal, longVal];

أستخدم أيضًا متغيرًا لقراءة قيمة المسافة داخل المسافة ، وتغيير الخطين الأخيرين إلى ما يلي:

float val = acos(sin(lat1rad) * sin(lat2rad) + cos(lat1rad) * cos(lat2rad) * cos(DEG2RAD(lon2) - DEG2RAD(lon1))) * 6378.1;
distanceVal = (float)val;

sqlite3_result_double(context, val);

أقوم بعد ذلك بحفظ المسافة في كائناتي التي تناسب المباراة ، وتطبيق نوع على العناصر التي تم إرجاعها:

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"distance" ascending:YES];
[ locations sortUsingDescriptors: [ NSArray arrayWithObject: sortDescriptor ] ];
[sortDescriptor release];
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top