Geofire Query on User Location

GeoFire query on User location

For GeoFire you must keep a segment in the tree that contains just the geolocation and then you have another segment in the tree that contains the other information about the items.

user_locations
uid1:
g: xxxx
l:
0: xxx
1: xxx
uid2:
g: xxxx
l:
0: xxx
1: xxx
user_profiles:
uid1:
name: "giacavicchioli"
uid2:
name: "Frank van Puffelen"

Also see: Query firebase data linked to GeoFire

Query Location by Radius with GeoFire

As it stands right now geofire sort of serves as an index to make geoqueries on, and provides the key of the document you want (which would be stored in a separate "collection").

You should be using geofire and a separate "collection" (call it eventPlaces)

var firebaseRef = firebase.database().ref('eventPlaces');
var geoFire = new GeoFire(firebaseRef);

Now you can use it as an index for your events, and can add items to it like so.

geoFire.set('-K_Pp-3RBJ58VkHGsL5P', [40.607765, -73.758949]);

Your Firebase RTDB will look like this now:

{
'events': {
'-K_Pp-3RBJ58VkHGsL5P': {
// All your data here
}
},
'eventPlaces': {
'-K_Pp-3RBJ58VkHGsL5P': {
'g': 'dr5x186m7u',
'l': [40.607765, -73.758949]
}
}
}

So finally when you do a query on your geoFire:

geoFire.query({
center: [40.607765, -73.758949],
radius: 10
}).on('key_entered', (key, location, distance) => {
console.log(key + ' entered query at ' + location + ' (' + distance + ' km from center)');
});

You'll end up being returned the key of the doc, for which you can do a normal Firebase query for that individual doc.

Retrieving list of users only within 3 miles using geofire

I solved it with the Help of answers from Frank Van Puffelen. Here's what I did

public void onKeyEntered(final String key, GeoLocation location) {

firebaseDatabase.child(key).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {

Users my=dataSnapshot.getValue(Users.class);
list.add(my);
adapter=new MyAdapter(MainActivity.this,list);
recycler.setAdapter(adapter);

}

How can i get user nearby my location in geofire,Firebase

Keep your GeoFire Locations separate from everything with a key to reference the other additional data e.g. user info or post info. As mixing static and dynamic data would not be the most efficient way of storing the data.

See my data structure and query here:

Retrieving Keys From GeoFire within Radius in Swift

Can't get near Users by my location with Firebase (GeoFire) and Swift

This answer depends on how you're user data is stored but if you're storing users with their documentId as the users uid then just

let uid = Auth.auth().currentUser!.uid //should safe unwrap that optional

then

geoFire.setLocation(CLLocation(..., forKey: uid)

however, if you want to get it as you're doing in the question

let uid = Auth.auth().currentUser!.uid
let usersCollection = Firestore.firestore().collection("users")
.whereField("uid", isEqualTo: uid)

usersCollection.getDocument(completion: { documentSnapshot, error in
if let err = error {
print(err.localizedDescription)
return
}

guard let docs = documentSnapshot?.documents else { return }

let thisUserUid = docs[0].documentID
print(thisUserUid)
})

But again, that's a bit redundant as it wound indicate storing the uid in both the documentId as well as a child field, which is unnecessary:

users
uid_0 //the documentId
name: "Users name"
uid: "uid_0" //not needed

The problem appers to be actually getting the center point - e.g. if it's not stored, then when read, it will be empty and you'll get that error.

So you have to store it to start with

geoFire.setLocation(CLLocation(latitude: 37.7853889, longitude: -122.4056973), forKey: uid) { (error) in
if (error != nil) {
print("An error occured: \(error)")
} else {
print("Saved location successfully!")
}
}

Once you successfully stored a center point (your location), and then retrieved it, then the actual query should be

let center = CLLocation(userLocation)
geoFire.queryAtLocation(center, withRadius: 20);

Oh, and a very important thing is the setLocation is asynchronous; it takes time for the server to store the data. You should really be working with the data within the closure

geoFire.setLocation(CLLocation(latitude: 37.7853889, longitude: -122.4056973), forKey: uid) { (error) in
// data is now valid so perform your query
}

Swift Firebase -How to get individual user's location from GeoFire

Inside the Database the GeoFire ref of each userId location has a "g" child and a "l" child:

@geoLocations
|
@--abc123xyz456 // userId
|
@--g: "dr72xyz25abc" // geoFire id for this user's location in geoFire
|
@--l
|--0: 40.870431300779900 // latitude
|--1: -73.090007211987188 // longitude

Here's a picture of the actual database layout

Sample Image

I have no idea what "g" stands for but I assume "l" stands for location because it's of type CLLocation as is stated in the arguments of .observe(.keyEntered, with: { (key: String!, location: CLLocation!).

Inside the database the 0 key and 1 are held as snapshot.value of array of either CLLocationDegrees or Double.

To get the latitude and longitude I used let arr = snapshot.value as? [CLLocationDegrees] but let arr = snapshot.value as? [Double] also worked.

Create a ref that has a child name of whatever the name of your geoLocations ref is > then add a child of the the userId > then add a child of "l" for the locations child.

Run observeSingleEvent(of: .value and in the callback cast snapshot.value as an an array of [CLLocationDegrees]

// *** if using CLLocationDegrees be to import CoreLocation ***
import CoreLocation

let geoLocationsRef = Database.database().reference()
.child("geoLocations") // name of my geoRef in Firebase
.child("abc123xyz456") // the userId I'm observing
.child("l") // the "l" is the child to observe

geoLocationsRef.observeSingleEvent(of: .value, with: { (snapshot) in

if !snapshot.exists() { return }

guard let arr = snapshot.value as? [CLLocationDegrees] else { return }

if arr.count > 1 {

let latitude = arr[0]
print(latitude)

let longitude = arr[1]
print(longitude)

// do whatever with the latitude and longitude
}
})

Here is the answer to my question with using dispatchGroup():

func queryLocationOfSubsetOfUsersInRadius() {

let dispatchGroup = DispatchGroup()

for user in subsetOfUsersInRadius {

dispatchGroup.enter()

let userId = user.userId

let geoLocationsRef = Database.database().reference()
.child("geoLocations")
.child(userId)
.child("l")

geoLocationsRef.observeSingleEvent(of: .value, with: { (snapshot) in

// this user may have deleted their location
if !snapshot.exists() {
dispatchGroup.leave()
return
}

guard let arr = snapshot.value as? [CLLocationDegrees] else {
dispatchGroup.leave()
return
}

if arr.count > 1 {

let latitude = arr[0]
print(latitude)

let longitude = arr[1]
print(longitude)

// do whatever with the latitude and longitude
}

dispatchGroup.leave()
})
}

dispatchGroup.notify(queue: .global(qos: .background)) {

// now animate the annotation from the user's inital old location (if they moved) on the mapView to their new location on the mapView. It's supposed to look like Uber's cars moving. Happens on main thread
}
}

how can i count the available user in specific loaction radius with geofire query?

The onDataEntered method is called for each node that is within the range. Once onDataEntered has been called for all nodes that are initially in range, the onGeoQueryReady method is called. That means that you can increment a counter in onDataEntered and then show it in onGeoQueryReady.

long count = 0;
geoQuery.addGeoQueryDataEventListener(new GeoQueryDataEventListener() {
@Override
public void onDataEntered(DataSnapshot dataSnapshot, GeoLocation location) {
count++;
}
@Override
public void onDataExited(DataSnapshot dataSnapshot) { }
@Override
public void onDataMoved(DataSnapshot dataSnapshot, GeoLocation location) { }
@Override
public void onDataChanged(DataSnapshot dataSnapshot, GeoLocation location) { }

@Override
public void onGeoQueryReady() {
ads5_userCounter.setText(""+count);
}

@Override
public void onGeoQueryError(DatabaseError error) {
throw databaseError.toException(); // don't ignore errors
}
});


Related Topics



Leave a reply



Submit