我有一个NSManagedObject派生类(实体),其实例持久存储在本地SQL-lite存储中。该类还具有经度和纬度属性,我需要根据特定坐标的距离来获取实体。我尝试将NSPredicate与自定义函数一起使用,但是我找不到关于如何实现该函数的文档(......如果它支持的话)。有没有人知道如何在核心数据实体上执行这种动态过滤?我尝试使用NSPredicate withBlock,但它不适用于SQL-Lite数据库上持久化的对象。请帮忙。
答案 0 :(得分:3)
您无法使用Core Data执行此操作。在获取实体后,您可以使用“瞬态”属性来模拟动态位置的距离,甚至可以根据“瞬态”属性对项目进行排序。但是,如果它们是持久属性,则只能从持久性存储中获取属性。
实际上,如果坐标被索引,我发现在矩形窗口中查询点非常快。通过获取动态位置的纬度/经度,加/减搜索半径来构建窗口,如果是未经投影的数据(纯lat / lon,不是UTM或类似网格),则为经度窗口投入余弦(纬度)调整。
如果真的不够快,你可以在你的实体上存储一个geohash,它会给你一个字符串你可以进行前缀搜索。有关讨论,请参阅https://en.wikipedia.org/wiki/Geohash。或者您可以实现真正的空间索引。但我从未遇到过使用这些方法的核心数据性能问题。
答案 1 :(得分:0)
我将我的位置作为纬度/经度坐标存储在数据模型中。然后我写了一些辅助扩展来找到lat / lon坐标的半矩形区域并通过它进行查询。这极大地限制了结果集,因此如果您需要按位置排序,则可以使用CLLocation距离计算器对生成的对象进行排序。
我创建了一个CLCircularRegion,你可以看到我创建查询的buildPredicate()
函数。
这是我正在使用的代码:
Swift 3
extension CLLocationDegrees {
static var north: CLLocationDegrees {
return 90.0
}
static var south: CLLocationDegrees {
return -90.0
}
static var east: CLLocationDegrees {
return 180.0
}
static var west: CLLocationDegrees {
return -180.0
}
var radians: Double {
return Double.pi * self / 180.0
}
}
extension CLLocationCoordinate2D {
var metersPerDegreeLatitude: CLLocationDistance {
return 111319.4907932736
}
var metersPerDegreeLongitude: CLLocationDistance {
return max(0.0, cos(self.latitude.radians) * self.metersPerDegreeLatitude)
}
}
extension CLCircularRegion {
var northernmostLatitude: CLLocationDegrees {
let longitude = self.center.latitude + self.radius / self.center.metersPerDegreeLatitude
return min(longitude, .north)
}
var southernmostLatitude: CLLocationDegrees {
let longitude = self.center.latitude - self.radius / self.center.metersPerDegreeLatitude
return max(longitude, .south)
}
var easternmostLongitude: CLLocationDegrees {
guard self.northernmostLatitude <= .north else {
return .east
}
guard self.southernmostLatitude >= .south else {
return .east
}
return min(.east, self.center.longitude + self.radius / (self.center.metersPerDegreeLongitude + 0.0001))
}
var westernmostLongitude: CLLocationDegrees {
guard self.northernmostLatitude <= .north else {
return .west
}
guard self.southernmostLatitude >= .south else {
return .west
}
return max(.west, self.center.longitude - self.radius / (self.center.metersPerDegreeLongitude + 0.0001))
}
func buildPredicate(latitudeName: String = "latitude", longitudeName: String = "longitude") -> NSPredicate {
let args = [self.southernmostLatitude, self.northernmostLatitude, self.westernmostLongitude, self.easternmostLongitude]
return NSPredicate(format: "\(latitudeName) >= %@ && \(latitudeName) <= %@ && \(longitudeName) >= %@ && \(longitudeName) <= %@", argumentArray: args)
}
}
答案 2 :(得分:0)
这里是答案https://www.objc.io/issues/4-core-data/core-data-fetch-requests/
static double const D = 80. * 1.1;
double const R = 6371009.; // Earth readius in meters
double meanLatitidue = pointOfInterest.latitude * M_PI / 180.;
double deltaLatitude = D / R * 180. / M_PI;
double deltaLongitude = D / (R * cos(meanLatitidue)) * 180. / M_PI;
double minLatitude = pointOfInterest.latitude - deltaLatitude;
double maxLatitude = pointOfInterest.latitude + deltaLatitude;
double minLongitude = pointOfInterest.longitude - deltaLongitude;
double maxLongitude = pointOfInterest.longitude + deltaLongitude;
request.result = [NSPredicate predicateWithFormat:
@"(%@ <= longitude) AND (longitude <= %@)"
@"AND (%@ <= latitude) AND (latitude <= %@)",
@(minLongitude), @(maxLongitude), @(minLatitude), @(maxLatitude)];
答案 3 :(得分:0)
这是上述答案的 Swift 版本
let D: Double = 80 * 1.1
let R: Double = 6371009
let meanLatitidue = pointOfInterest.coordinate.latitude * .pi / 180
let deltaLatitude = D / R * 180 / .pi
let deltaLongitude = D / (R * cos(meanLatitidue)) * 180 / .pi
let minLatitude: Double = pointOfInterest.coordinate.latitude - deltaLatitude
let maxLatitude: Double = pointOfInterest.coordinate.latitude + deltaLatitude
let minLongitude: Double = pointOfInterest.coordinate.longitude - deltaLongitude
let maxLongitude: Double = pointOfInterest.coordinate.longitude + deltaLongitude
let predicate = NSPredicate(format: "(%lf <= longitude) AND (longitude <= %lf) AND (%lf <= latitude) AND (latitude <= %lf)", minLongitude, maxLongitude,minLatitude, maxLatitude)