Reputation: 3635
I have warpper class for CLLoaction
Wrapper
class Location: Object {
dynamic var long: Double = 0
dynamic var lat: Double = 0
}
I have to filter stored Location
for those which are in 1km radius based on my current location. I thought NSPredicate with block would do the job, but realm doesn't support it. So my question is how other way I can achieve it?
Of course I could do something like this:
let locations = realm.objects(Location)
var locationsInRadius = [Location]()
for l in locations {
let location = CLLocation(latitude: l.lat, longitude: l.long)
if (location.distanceFromLocation(currentLocation) < radius){
locationsInRadius.append(l)
}
}
But it feels wrong according to whole realm concept of filters.
Upvotes: 1
Views: 2216
Reputation: 833
For some reason, using a single predicate (lat BETWEEN {%f, %f} AND lon BETWEEN {%f, %f}
) doesn't work with the current version of Realm. I'm using this nice lib: https://github.com/mhergon/RealmGeoQueries
This is how the predicate is built inside and it works fine: https://github.com/mhergon/RealmGeoQueries/blob/master/GeoQueries.swift:
let topLeftPredicate = NSPredicate(format: "%K <= %f AND %K >= %f", latitudeKey, box.topLeft.latitude, longitudeKey, box.topLeft.longitude)
let bottomRightPredicate = NSPredicate(format: "%K >= %f AND %K <= %f", latitudeKey, box.bottomRight.latitude, longitudeKey, box.bottomRight.longitude)
let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [topLeftPredicate, bottomRightPredicate])
Basically, here is how to use it:
let results = try! Realm()
.findNearby(YourModelClass.self, origin: location.coordinate, radius: 500, sortAscending: nil)
You can also change the default "lat" and "lng" keys by passing 2 extra parameters with your own naming (latitudeKey and longitudeKey).
Thanks to https://github.com/mhergon for providing this lib.
Upvotes: 2
Reputation: 15163
You can't search for objects by distance, but you can search by using a bounding box. Simply add latitude
and longitude
fields to your object, then:
In code, that could like this:
// 0. This example uses MapKit to calculate the bounding box
import MapKit
// 1. Plenty of answers for this one...
let currentLocation = CLLocationCoordinate2DMake(37.7749295, -122.4194155)
// 2. Create the bounding box with, 1km radius
let region = MKCoordinateRegionMakeWithDistance(currentLocation, 1000, 1000)
let northWestCorner = CLLocationCoordinate2DMake(
currentLocation.latitude + (region.span.latitudeDelta),
currentLocation.longitude - (region.span.longitudeDelta)
)
let southEastCorner = CLLocationCoordinate2DMake(
currentLocation.latitude - (region.span.latitudeDelta),
currentLocation.longitude + (region.span.longitudeDelta)
)
// 3. Filter your objects
let predicate = NSPredicate(format: "lat BETWEEN {%f, %f} AND lon BETWEEN {%f, %f}",
northWestCorner.latitude,
southEastCorner.latitude,
northWestCorner.longitude,
southEastCorner.longitude
)
let nearbyLocations = realm.objects(MyLocation).filter(predicate)
Note that you can still store your CLLocation
object for other purposes, but you won't need it for the search.
Also note that as this searches a box rather than what you wanted, a circle with a 1km radius, this can return results of greater than 1km. If that's not ok, you would need to reduce the radius or make a fancier predicate.
Upvotes: 4