Geofire - not found locations
Asked Answered
B

3

7

How can I get the all locations that are near to a passed GeoLocation(double lat, double lng) using Geo Queries. I have the following code (and it happens nothing):

public void setCurrentLatLng(double lat, double lng){
    this.lat = lat;
    this.lng = lng;
    GeoLocation geoLocation = new GeoLocation(lat, lng);
    updateCurrenLocation(geoLocation);
    GeoQuery geoQuery = geoFire.queryAtLocation(geoLocation, 8f);
    geoQuery.addGeoQueryDataEventListener(new GeoQueryDataEventListener() {

        @Override
        public void onDataEntered(DataSnapshot dataSnapshot, GeoLocation location) {
            Log.d("geoQuery","onDataEntered "+dataSnapshot.toString());
            // ...
        }

        @Override
        public void onDataExited(DataSnapshot dataSnapshot) {
            Log.d("geoQuery","onDataExited "+dataSnapshot.toString());
            // ...
        }

        @Override
        public void onDataMoved(DataSnapshot dataSnapshot, GeoLocation location) {
            Log.d("geoQuery","onDataMoved "+dataSnapshot.toString());
            // ...
        }

        @Override
        public void onDataChanged(DataSnapshot dataSnapshot, GeoLocation location) {
            Log.d("geoQuery","onDataChanged "+dataSnapshot.toString());
            // ...
        }

        @Override
        public void onGeoQueryReady() {
            // ...
            Log.d("geoQuery","onGeoQueryReady");
        }

        @Override
        public void onGeoQueryError(DatabaseError error) {
            Log.d("geoQuery","onGeoQueryError");
            // ...
        }

    });
    this.setChanged();
    notifyObservers();
    this.clearChanged();
    Log.d("update","clearChanged");
}

And this is my firebase data: enter image description here

I think I'm open to modify the data struct if needed.

Logs

09-12 08:55:33.818 17710-17710/es.rchampa.weirdo D/geoQuery: lat=40.4430883 lng=-3.721805
09-12 08:55:33.982 17710-17710/es.rchampa.weirdo D/geoQuery: lat=40.4430883 lng=-3.721805
09-12 08:55:33.986 17710-17710/es.rchampa.weirdo D/geoQuery: onGeoQueryReady
09-12 08:55:34.025 17710-17710/es.rchampa.weirdo D/geoQuery: onGeoQueryReady

Gradle file

....
// Firebase
implementation 'com.google.firebase:firebase-database:16.0.1'
implementation 'com.google.firebase:firebase-storage:16.0.1'
implementation 'com.google.firebase:firebase-auth:16.0.3'
implementation 'com.google.firebase:firebase-crash:16.2.0'
implementation 'com.google.firebase:firebase-core:16.0.3'

// Firebase UI
implementation 'com.firebaseui:firebase-ui-database:1.2.0'

//Firebase GeoFire
implementation 'com.firebase:geofire-android:2.3.1'

// Google Play Services
implementation 'com.google.android.gms:play-services-auth:16.0.0'
implementation 'com.google.android.gms:play-services-maps:15.0.1'
implementation 'com.google.android.gms:play-services-location:15.0.1'
....

UPDATE

I can grant access to my private repo if you wish.

Barfuss answered 9/9, 2018 at 23:28 Comment(13)
Have you tried to log the error from onGeoQueryError() method? What does it print?Footpace
@AlexMamo I've updated the code to add logs. Only onGeoQueryReady is executed. Any help?Barfuss
I think the way you have designed the database is creating problems. https://mcmap.net/q/1622694/-storing-extra-data-under-geofire-nodesPontificate
Can you add some logs for lat, lng, geolocation, as well as geoquery and POST the log here.Cherlynchernow
@Pontificate the design got no problem medium.com/google-cloud/…Cherlynchernow
@AngusTay done.Barfuss
@ricardo, Database should be designed as i.sstatic.net/CrPRQ.jpgPontificate
@ricardo, can you post the code where you have set up geoFire object?Pontificate
Don't worry about the data structure, nothing wrong and it's commonCherlynchernow
Correct me if I'm wrong, you want to show all the other users that are located 8km around the current user location right?Cherlynchernow
@AngusTay right!Barfuss
So the actual problem now is displaying users around? The current location is updated in the database?Cherlynchernow
@Barfuss just added a working example.Greg
G
3

you pass value 8f (float) as the radius there, while the the radius should rather be 8.0d or Double.valueOf(8.0), where MAX_SUPPORTED_RADIUS equals 8587 kilometers.

while the actual problem is, that GeoFire already would need to know of .child("location"), but it is not possible to represent that with a Reference; only DataSnapshot has getChildren().

the bottom line is:

you'd have to create a separate locations Reference, in order to avoid the nesting. nevertheless you still can use the related uid key for these nodes (or at least add it as a child-node), in order to be able to look up within the users Reference. it's a 1:1 relation, in between two References.

so here's a working Java example, just because ...

we're assuming the following structure (as described above):

{
  "locations" : {
    "CR88aa9gnDfJYYGq5ZTMwwC38C12" : {
      ".priority" : "9q8yywdgue",
      "g" : "9q8yywdgue",
      "l" : [ 37.7853889, -122.4056973 ]
    }
  },
  "users" : {
    "CR88aa9gnDfJYYGq5ZTMwwC38C12" : {
      "displayName" : "user 01",
      ...
    }
  }
}

the database rules should have .indexOn for locations field g set:

{
  "rules": {
    ...
    "locations": {
      ".indexOn": "g"
    }
  }
}

the dependencies in the module's build.gradle:

dependencies {
    ...
    implementation "com.firebase:geofire-android:2.3.1"
}

and this demonstrates, how to obtain a user's snapshot by a GeoQuery result;

notice the GeoQueryEventListener instead of the GeoQueryDataEventListener:

public class GeofireActivity extends AppCompatActivity {

    private static final String LOG_TAG = GeofireActivity.class.getSimpleName();

    private DatabaseReference refBase     = null;
    private DatabaseReference refLocation = null;
    private DatabaseReference refUser     = null;

    private GeoFire geoFire = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setContentView(R.layout.fragment_geofire);
        this.setReferences();
    }

    private void setReferences() {
        this.refBase = FirebaseDatabase.getInstance().getReference();
        this.refUser = refBase.child("users");
        this.refLocation = refBase.child("locations");
        this.geoFire = new GeoFire(this.refLocation);
    }

    private void searchNearby(double latitude, double longitude, double radius) {
        this.searchNearby(new GeoLocation(latitude, longitude), radius);
    }

    private void searchNearby(GeoLocation location, double radius) {

        GeoQuery geoQuery = this.geoFire.queryAtLocation(location, radius);
        geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {

            @Override
            public void onKeyEntered(String key, GeoLocation location) {

                String loc = String.valueOf(location.latitude) + ", " + String.valueOf(location.longitude);
                Log.d(LOG_TAG, "onKeyEntered: " + key + " @ " + loc);

                /* once the key is known, one can lookup the associated record */
                refUser.child(key).addListenerForSingleValueEvent(new ValueEventListener() {

                    @Override
                    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                        Log.d(LOG_TAG, "onDataChange: " + dataSnapshot.toString());
                    }

                    @Override
                    public void onCancelled(@NonNull DatabaseError firebaseError) {
                        Log.e(LOG_TAG, "onCancelled: " + firebaseError.getMessage());
                    }
                });
            }

            @Override
            public void onKeyExited(String key) {
                Log.d(LOG_TAG, "onKeyExited: " + key);
            }

            @Override
            public void onKeyMoved(String key, GeoLocation location) {
                Log.d(LOG_TAG, "onKeyMoved: " + key);
            }

            @Override
            public void onGeoQueryReady() {
                Log.d(LOG_TAG, "onGeoQueryReady");
            }

            @Override
            public void onGeoQueryError(DatabaseError error) {
                Log.e(LOG_TAG, "onGeoQueryError" + error.getMessage());
            }
        });
    }
}

in order to maintain the integrity, one would need to remove the associated location record, when a user record is being removed - else it would result in keys, which cannot be looked up anymore.

Greg answered 12/9, 2018 at 7:10 Comment(5)
I've updated the post because still can't find locations.Barfuss
@Barfuss which version of the library do you use and which listener? your code still uses the GeoQueryDataEventListener and I think there is a difference in between the GeoFire v1 and v2 libraries (that's why I posted the example code as well, which works for me).Greg
I'm using: com.firebase:geofire-android:2.3.1Barfuss
@Barfuss try implementing the GeoQueryEventListener instead, which has whole other events - and a whole other meaning; the other one GeoQueryDataEventListener is good for being notified of server-side changes, eg. when another client changes the data.Greg
My bad. I was using a wrong path reference. Now is working! Thanks.Barfuss
R
2

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 usersLocations)

DatabaseReference ref = FirebaseDatabase.getInstance().getReference("usersLocations");
GeoFire geoFire = new GeoFire(ref);

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

geoFire.setLocation('QymlMpC0Zc', new GeoLocation(40.2334983, -3.7185183));

Your Firebase RTDB will look like this now:

{
   'users': {
        'QymlMpC0Zc': {
            // All your data here
        }
    },
   'usersLocations': {
        'QymlMpC0Zc': {
            'g': 'ezjkgkk305',
            'l': {
                '0': 40.2334983,
                '1': -3.7185183
            }
        }
    }
}

So finally when you do a query on your geoFire you'll end up being firing whatever listeners you have.

As a small note... I am not a Java developer, but I do use/know geofire in general. Hopefully my bits of advice/thoughts will be helpful.

Roane answered 14/9, 2018 at 18:1 Comment(1)
I've updated the post because still can't find locations.Barfuss
C
2

The Problem is When You Passed Radius According to Issue https://github.com/firebase/geofire-java/issues/72

    double radius = 8589; // Fails
//  double radius = 8587.8; //Passes

try to pass value like this this may helps

//GeoQuery geoQuery = geoFire.queryAtLocation(geoLocation, 8f);
GeoQuery geoQuery = geoFire.queryAtLocation(geoLocation, radius);

passing value 8f (float) as the radius, while the the radius should rather be 8.0d or Double.valueOf(8.0), where MAX_SUPPORTED_RADIUS equals 8587 kilometers.

Corded answered 15/9, 2018 at 18:42 Comment(1)
I've updated the post because still can't find locations.Barfuss

© 2022 - 2024 — McMap. All rights reserved.