先前近似的问题
这里的上一个答案将地球建模为笛卡尔平面,经纬度分别为x和y,然后围绕点绘制圆形或矩形---实际形状无关紧要,两种方法都有相同的属性。
如果您的约束真的很松散,这可能很好,并且它们可能在您的特定实例中出现。为了让这个答案对每个人都更有用,我已经回答了这个问题,假设你的约束没有那么松散。对于移动地理定位应用程序,这种近似通常会导致问题。也许您已经看到了一个错误的应用程序。由此产生的距离没有任何意义。以前的方法有几个缺陷:
- 错误的近似值:地球的规模很大,所以简单的近似值可能会偏离数英里。如果用户在车里,这很糟糕,如果用户步行,这真的很糟糕。带有度数的简单技巧并不能改善圆形或正方形:纬度和经度分布不均匀,您需要像 Haversine 这样的完整近似值才能获得准确的数据。
- 排除好点:除非您严重扩展形状,否则您的样本将排除有效点。
- 包括坏点:扩展形状以捕获排除的有效点越多,捕获的无效点就越多。
- 损坏的排序顺序:如果没有真正的计算,您将无法正确按距离对输出进行排序。这意味着您生成的任何有序列表都将被取消。通常,您希望这些点按接近的顺序排列。
- 两次计算:如果您返回并修复它,您将进行两次计算。
C/Objective-C 中的单点半正弦
选择代码已经在另一个答案中。这是您引用的重新计算,您必须在 Objective-C 或 C 之后为每个点执行:
/////////////////////////////////////
//Fill these in or turn them into function arguments
float lat1=center_point.latitude;
float lon1=center_point.longitude;
float lat2=argument_point.latitude;
float lon2=argument_point.longitude;
/////////////////////////////////////
//Conversion factor, degrees to radians
float PI=3.14159265358979;
float f=PI/180;
lat1*=f; lon1*=f; lat2*=f; lon2*=f;
//Haversine Formula (from R.W. Sinnott, "Virtues of the Haversine", Sky and Telescope, vol. 68, no. 2, 1984, p. 159):
float dlon = lon2 - lon1;
float dlat = lat2 - lat1;
float a = pow((sin(dlat/2)),2) + cos(lat1) * cos(lat2) * pow((sin(dlon/2)),2);
float c = 2 * asin(MIN(1,sqrt(a)));
float R = 6378.137;//km Radius of Earth
float d = R * c;
d *= 0.621371192; //optional: km to miles [statute] conversion factor
//NSString conversion?
//if (d >= .23) return [NSString stringWithFormat:@"%0.1f m",d]; //.23m ~ 400 yds
//d *= 5280; //miles to feet conversion factor
//d /= 3; //feet to yards
//int y=(int)d;
//return [NSString stringWithFormat:@"%d yds",y];
return d;
这应该是完成所讨论的任务所需的所有工具。
SQLite 中的Haversine
我想向您展示一个直接的仅限 SQLite 的解决方案,但我始终无法让 Haversine 直接在 SQLite 中令人满意地运行。您在 SQLite 中没有平方根。你没有得到 pow() 函数,尽管你可以重复一个参数本身。你不会得到 sin、cos 或 sinh。有一些扩展可以添加其中一些功能。我不知道与基本 SQLite 相比,它们的支持程度如何。即使有它们,它也会太慢。
人们似乎建议使用预先计算的正弦值更新列。只要您不需要对整列求正弦值就可以了,在这种情况下,每次需要进行新计算时,您都在向表中写入一个新列,这很糟糕。无论如何,我想向您展示一下 SQLite 在计算 Haversine 时的速度有多慢,但我根本无法让它计算它。 (我认为我对 SQLite 的记忆很慢实际上是服务器上的 MySQL 的记忆很慢。)
Kerf中的全点解决方案
我希望前面的讨论是对标准工具可以做什么的近乎详尽的了解。
好消息是,如果您操作正确,此计算在手机上会很快。我为您构建了一个东西,我认为它可以更好地解决您的问题。如果你愿意使用其他工具,在Kerf 这个问题很简单。我回去并致力于三角函数的回购矢量化操作,以便计算速度更快。我的 iPhone 5 将在 20 毫秒内完成 10,000 个点,在 150 毫秒内完成 100,000 个点。您可以在 1.5 秒内完成 100 万分,但此时您需要一个 throbber。按照规则披露:我建立了它。
来自 Kerf REPL:
//ARTIFICIAL POINT GENERATION ///////////////////
n: 10**4
point_lat: 80 + rand(40.0)
point_lon: 80 + rand(40.0)
mytable: {{lats: 60 + rand(n, 60.0), lons: 60 + rand(n, 60.0)}}
lats : mytable.lats
lons : mytable.lons
/////////////////////////////////////
//COMPUTATION////////////////////////
dlon: lons - point_lon
dlat: lats - point_lat
distances_km: (6378.137 * 2) * asin(mins(1,sqrt(pow(sin(dlat/2),2) + cos(point_lat) * cos(lats) * pow(sin(dlon/2) ,2))))
//distances_miles: 0.621371192 * distances_km //km to miles [statute] conversion
//sort_order: ascend distances_km
或通过 Kerf iOS SDK。删除语句末尾的分号将允许您将其作为 JSON 记录到终端。
KSKerfSDK *kerf = [KSKerfSDK new];
kerf.showTimingEnabled = YES;
//Sample Data Generation
[kerf jsonObjectFromCall:@"n: 10**4;"];
[kerf jsonObjectFromCall:@"point_lat: 80 + rand(40.0);"];
[kerf jsonObjectFromCall:@"point_lon: 80 + rand(40.0);"];
[kerf jsonObjectFromCall:@"mytable: {{lats: 60 + rand(n, 60.0), lons: 60 + rand(n, 60.0)}};"];
[kerf jsonObjectFromCall:@"lats : mytable.lats;"];
[kerf jsonObjectFromCall:@"lons : mytable.lons;"];
//Computation
[kerf jsonObjectFromCall:@"dlon: lons - point_lon;"];
[kerf jsonObjectFromCall:@"dlat: lats - point_lat;"];
NSLog(@"%@", [kerf jsonObjectFromCall:@"distances_km: (6378.137 * 2) * asin(mins(1,sqrt(pow(sin(dlat/2),2) + cos(point_lat) * cos(lats) * pow(sin(dlon/2) ,2)))); "]);