Search code examples
androidfirebasefirebase-realtime-databasegeofire

How can I retrieve nearby bakeries to users using firebase


I know that this question has been asked over and over but I just can't seem to get a good answer. I am trying to retrieve data based off of the users location. For example lets say my database has a list of bakeries. Each bakery has an address, the longitude and latitude,postal code,city,state. Now I want to display all of the bakeries to customers but only the ones that are around their area lets say 5 miles away how would I go about doing that. I've been looking for answers for the past few days and nothing seems to be working. I even tried this answer I saw on Stack overflow and its not working. Someone please please please help me with this. Below is my code Firebase Database

//This page is where after the user registers it will automatically save the longitude and latitude and that's when it will save the Geohash etc

     mFusedLocationClient.getLastLocation()
                .addOnSuccessListener(this, new OnSuccessListener<Location>() {
                    @Override
                    public void onSuccess(final Location location) {
                        // Got last known location. In some rare situations this can be null.
                        if (location != null) {
                            // Logic to handle location object
                            Double latittude = location.getLatitude();
                            Double longitude = location.getLongitude();

                            String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
                            DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference("Users");
                            DatabaseReference update = rootRef.child(uid);
                            GeoFire geoFire=new GeoFire(rootRef);

                            geoFire.setLocation("latandlong", new GeoLocation(location.getLatitude(), location.getLongitude()), new GeoFire.CompletionListener() {
                                @Override
                                public void onComplete(String key, DatabaseError error) {
                                    if (error != null) {
                                        System.err.println("There was an error saving the location to GeoFire: " + error);
                                    } else {
                                        System.out.println("Location saved on server successfully!");
                                    }
                                }
                            });


                            geoFire.getLocation("latandlong", new LocationCallback() {
                                @Override
                                public void onLocationResult(String key, GeoLocation location) {
                                    if (location != null) {
                                        System.out.println(String.format("The location for key %s is [%f,%f]", key, location.latitude, location.longitude));
// save longitude and latitude to db

                                        String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
                                        DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
                                        DatabaseReference update = rootRef.child("Users").child(uid);
                                        Double longi = location.longitude;
                                        Double lat = location.latitude;




                                        update.child("latandlong").setValue(location.latitude+ "," +location.longitude);
                                        //update.child("longitude").setValue(longi);
                                        //update.child("latitude").setValue(lat);
                                    } else {
                                        System.out.println(String.format("There is no location for key %s in GeoFire", key));
                                    }
                                }

                                @Override
                                public void onCancelled(DatabaseError databaseError) {
                                    System.err.println("There was an error getting the GeoFire location: " + databaseError);
                                }
                            });



     protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_news_feed);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);



        menuItem=(MenuItem)findViewById(R.id.item_sign_in);




        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);


        recycler=findViewById(R.id.recyclerView);
        recycler.setLayoutManager(new LinearLayoutManager(this));

        final DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();

        mFusedLocationClient.getLastLocation().addOnSuccessListener(new OnSuccessListener<Location>() {
            @Override
            public void onSuccess(Location location) {

                Double lat=location.getLatitude();
                Double longi=location.getLongitude();

                firebaseDatabase=FirebaseDatabase.getInstance().getReference("Users");
                GeoFire geoFire=new GeoFire(firebaseDatabase.child("latandlong"));

                GeoQuery geoQuery=geoFire.queryAtLocation(new GeoLocation(lat,longi),5);

                geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
                    @Override
                    public void onKeyEntered(String key, GeoLocation location) {
                        firebaseDatabase.child(key).addValueEventListener(new ValueEventListener() {
                            @Override
                            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                                if(dataSnapshot.exists()){
                                    list=new ArrayList<UserInformation>();

                                    for(DataSnapshot dataSnapshot1:dataSnapshot.getChildren()){
                                        UserInformation uuu=dataSnapshot1.getValue(UserInformation.class);
                                        list.add(uuu);

                                    }
                                }

                                adapter=new MyAdapter(NewsFeedActivity.this,list);
                                recycler.setAdapter(adapter);

                            }

                            @Override
                            public void onCancelled(@NonNull DatabaseError databaseError) {

                            }
                        });
                    }

                    @Override
                    public void onKeyExited(String key) {

                    }

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

                    }

                    @Override
                    public void onGeoQueryReady() {

                    }

                    @Override
                    public void onGeoQueryError(DatabaseError error) {

                    }
                });




            }
        });

Solution

  • This is how you initialize GeoFire:

    firebaseDatabase=FirebaseDatabase.getInstance().getReference("Users");
    GeoFire geoFire=new GeoFire(firebaseDatabase.child("latandlong"));
    

    This means that in your database you're expected to have a node /Users/latandlong, under which you have a node with the geohash and lat/lon for each key you want to track. So:

    Users: {
      latandlong: {
        "IEvLcwll....SQkbp2": {
          g: "..."
          l: { 0: ..., 1: ... }
        }
      }
    }
    

    This structure is different from what's in the screenshot you shared, as you are missing the key level: "IEvLcwll....SQkbp2". This explains why any geoquery you fire on geoFire won't find any keys.

    I highly recommend reading the GeoFire documentation on:

    1. How to initialize the GeoFire object, which you'll typically want to point to a top-level node.
    2. Then set the location data for a specific key, which will then become a child node under the top-level node you set in the previous step.

    Only once you've done these two steps correctly, will you be able to execute a geoquery against the data.