Filter Firebase Database based on location radius
Asked Answered
S

3

6

I have the following Firebase Database structure,

"-KSupX7CppMD1zbqIy0y" : {
  "addedByUser" : "zQpb7o18VzYsSoTQtT9DNhOqTUn2",
  "content" : "Post 1",
  "cost" : "20",
  "duration" : "Weekly",
  "latitude" : "40.7594479995956",
  "longitude" : "-73.9838934062393",
  "timestamp" : "Fri 30 Sep"
},
"-KSuphuqO6a0lnrJYUkt" : {
  "addedByUser" : "zQpb7o18VzYsSoTQtT9DNhOqTUn2",
  "content" : "Post 2",
  "cost" : "10",
  "duration" : "Daily",
  "latitude" : "40.7594329996462",
  "longitude" : "-73.9846261181847",
  "timestamp" : "Fri 30 Sep"
}

I need to filter the posts within certain distance parameters (less than 50m, 100m, 150m & 200m) I can get the coordinate from the "longitude" & "latitude" but I'm struggling to filter the posts. Any help would be greatly appreciated.

Many thanks in advance. Newbie to Firebase & Swift.

Statehood answered 30/9, 2016 at 12:55 Comment(1)
Have a look at Geofire: github.com/firebase/geofire-objcAmman
S
2

For JSON like this:-

 UsersLocation :{
     autoID1 : {....},
     autoID2 : {.....}
           }

Try this:-

    FIRDatabase.database().reference().child("UsersLocation").queryOrdered(byChild: "lat").queryStarting(atValue: 30).queryEnding(atValue: 60).observeSingleEvent(of: .value, with: {(snap) in

        print(snap)


        if let snapDict = snap.value as? [String:AnyObject]{

            for each in snapDict{

                print(each)

            }
        }
    })
}

This will give you all the users with a autoID Key, with their latitude between 30 and 60, The order in which you retrieve data will be random, so to iterate through them in ascending order you can sort your dictionary that you receive .

Sebastiansebastiano answered 1/10, 2016 at 2:46 Comment(2)
Accept this as answer if it solved your issue :) Happy CodingSebastiansebastiano
Could you expand this answer to also include filtering by longitude?Careycarfare
P
0

Below is a solution you could try. This assumes you have all posts downloaded to the app from Firebase and stored in an array. You could also look into using GeoFire which I have not personally used (But would like to). I believe it allows you to query Firebase by location so you do not first have to download all posts to a user's device and filter on the client side.

// assumes you have set up the app to get the user's current location
var currentLocation: CLLocation?

// Your array of posts downloaded from Firebase
var postArray = [post]()

let metersInTwentyFiveMiles = 40233.6

// You could put this in viewDidAppear after you load the post array from Firebase
for post in postArray {

    var distanceInMeters: CLLocationDistance = 0

    let postLocation = CLLocation(latitude: post.latitude, longitude: post.longitude)


    if let currentLocation = self.currentLocation {
         // Set distanceInMeters to the distance between the user's current location and the location of the post.
         distanceInMeters = currentLocation.distanceFromLocation(postLocation)
         // If this distance is not within 25 miles, remove this post from the array
         if self.metersInTwentyFiveMiles < distanceInMeters {
            self.posts = self.posts.filter{ $0 != post }
       }
    }
}
Partain answered 30/9, 2016 at 14:10 Comment(1)
The problem is that if your app is massively popular and there is tons of data, then downloading all the data in order to filter by distance would be quite a strain..... very inefficient.Waldowaldon
T
0

Use GeoHash

Here is full documentation for it you can use single https://firebase.google.com/docs/firestore/solutions/geoqueries

Add this to your Podfile pod 'GeoFire/Utils'

Insert GeoHash

let latitude = 51.5074
let longitude = 0.12780
let location = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)

let hash = GFUtils.geoHash(forLocation: location)

// Add the hash and the lat/lng to the document. We will use the hash
// for queries and the lat/lng for distance comparisons.
let documentData: [String: Any] = [
    "geohash": hash,
    "lat": latitude,
    "lng": longitude
]

let londonRef = db.collection("cities").document("LON")
londonRef.updateData(documentData) { error in
    // ...
}

Get filter data

let queries = queryBounds.map { bound -> Query in
    return db.collection("cities")
        .order(by: "geohash")
        .start(at: [bound.startValue])
        .end(at: [bound.endValue])
}
Treachery answered 25/11, 2022 at 11:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.