У меня есть производный класс (сущность) NSManagedObject, экземпляры которого хранятся в локальном хранилище SQL-lite. Класс также имеет свойства долготы и широты, и мне нужно получить объекты на основе расстояния от конкретных координат. Я попытался использовать NSPredicate с пользовательскими функциями, но я не смог найти документацию о том, как реализовать функцию (... если она это поддерживает). Кто-нибудь знает, как выполнить такую динамическую фильтрацию на объектах Core Data? Я пытался с NSPredicate withBlock, но он не работает с объектами, которые сохраняются в базе данных SQL-Lite. Пожалуйста помоги.Отфильтровать объекты данных ядра на расстоянии от динамического положения
ответ
Вы не можете сделать это с помощью Core Data. Вы можете использовать свойство Transient для моделирования расстояния от вашего динамического местоположения, после того, как объекты были извлечены, и даже упорядочить элементы на основе этого свойства Transient. Но вы можете только извлекать свойства из постоянного хранилища, если они являются постоянными свойствами.
На практике я обнаружил, что очень быстро запрашивать точки в прямоугольном окне, если координаты индексируются. Постройте окно, выбрав широту/долготу динамической позиции, плюс/минус радиус поиска, введя корректировку косинуса (широты) для окна долготы, если это непроектированные данные (чистый lat/lon, а не UTM или подобная сетка).
Если это действительно не так быстро, вы можете сохранить geohash на своих сущностях, что дает вам строку, которую вы можете использовать в префиксном поиске. См. https://en.wikipedia.org/wiki/Geohash для обсуждения. Или вы можете реализовать истинный пространственный индекс. Но у меня никогда не было проблемы с базовыми данными, что сделало его целесообразным для реализации любого из этих подходов.
Я сохраняю свои местоположения в модели данных в виде координат широты/долготы. Затем я написал некоторые вспомогательные расширения, чтобы найти полупрямую область координат 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)
}
}
Нет именно то, что мне нужно ... ТХ – Sergiob