5

I am saving the coordinates of the post posted by the users. I'm generating a push id and then using it to save both the data of the post and the geofire coordinates.

I want to show only those posts which are within 0.5 kms radius of them. I am using GeoFire library for the same but I'm unable to achieve the task.

Here's how I'm generating the push id:

itemID = databaseReferenceRequests.push().getKey();

Here's how I'm using it to save geofire coordinates as well as the data of the posts:

geoFire.setLocation(itemID, 
        new GeoLocation(Double.parseDouble(currentLat.getText().toString()), 
        Double.parseDouble(currentLng.getText().toString())));

databaseReferenceRequests.child(itemID).setValue(hRequest);

It is getting saved like this:

enter image description here

The problem is that when I'm trying to fetch only those posts which are within 0.5 kms of my reach, it is not happening and all the posts whether near or far are getting retrieved.

Here's how I'm retrieving it:

public void retrieveHelpRequests() {

            geoQuery = geoFire.queryAtLocation(new GeoLocation(currentLatDouble, currentLngDouble), 0.5);

            geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
                @Override
                public void onKeyEntered(String key, GeoLocation location) {
                    databaseReference.child("help-requests").addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        Map<String, String> newRequest = (Map<String, String>) dataSnapshot.getValue();
                        imageUID = newRequest.get("imageUIDh");
                        homelessDescription = newRequest.get("homelessDescription");
                        currentLat = newRequest.get("currentLat");
                        currentLng = newRequest.get("currentLng");
                        postedBy = newRequest.get("postedBy");
                        postedAtTime = newRequest.get("postedAtTime");
                        postedOnDate = newRequest.get("postedOnDate");
                        utcFormatDateTime = newRequest.get("utcFormatDateTime");

                        String timeStr = utcFormatDateTime;
                        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        df.setTimeZone(TimeZone.getTimeZone("UTC"));
                        Date date = null;
                        try {
                            // error on line below
                            date = df.parse(timeStr);
                        } catch (ParseException e) {
                            e.printStackTrace();
                        }
                        df.setTimeZone(TimeZone.getDefault());
                        final String persisted = df.format(date);

                        // Parse string from DB - UTC timezone
                        Date parsed = null;
                        try {
                            parsed = df.parse(persisted);
                        } catch (ParseException e) {
                            e.printStackTrace();
                        }

                        // Now convert to whatever timezone for display purposes
                        final SimpleDateFormat displayFormat = new SimpleDateFormat("h:mm a");
                        displayFormat.setTimeZone(TimeZone.getDefault());

                        formattedTime = displayFormat.format(parsed);

                        prepareDataForRequests();
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {
                        Snackbar snackbar = Snackbar
                                .make(coordinatorLayout, databaseError.getMessage(), Snackbar.LENGTH_LONG);
                        snackbar.setDuration(Snackbar.LENGTH_SHORT);
                        snackbar.show();
//                helpRequestsLoadingDialog.dismiss();
                        progressBarLoadingRequests.setVisibility(View.INVISIBLE);
                    }
                });

                    databaseReference.child("help-requests").addValueEventListener(new ValueEventListener() {
                        @Override
                        public void onDataChange(final DataSnapshot dataSnapshot) {
    //                adView.loadAd(request);
                            card_ads2.setVisibility(View.VISIBLE);
                            adView2.loadAd(request2);
                            if (snackbar != null) {
                                snackbar.dismiss();
                            }
                            progressBarLoadingRequests.setVisibility(View.INVISIBLE);

                            if (fastItemAdapter.getAdapterItemCount() == 0) {
                                emptyRVtext.setVisibility(View.VISIBLE);
                                emptyRVtexthh.setVisibility(View.VISIBLE);
                                card_ads2.setVisibility(View.INVISIBLE);
                            } else {
                                emptyRVtext.setVisibility(View.INVISIBLE);
                                emptyRVtexthh.setVisibility(View.INVISIBLE);
                            }

    //                progressBarLoadingRequests.setVisibility(View.INVISIBLE);
                        }

                        @Override
                        public void onCancelled(DatabaseError databaseError) {
                            Snackbar snackbar = Snackbar
                                    .make(coordinatorLayout, databaseError.getMessage(), Snackbar.LENGTH_LONG);
                            snackbar.setDuration(Snackbar.LENGTH_SHORT);
                            snackbar.show();
    //                hRequestsLoadingDialog.dismiss();
                            progressBarLoadingRequests.setVisibility(View.INVISIBLE);
                        }
                    });
                }

                @Override
                public void onKeyExited(String key) {

                }

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

                }

                @Override
                public void onGeoQueryReady() {

                }

                @Override
                public void onGeoQueryError(DatabaseError error) {
                    Toast.makeText(getBaseContext(), "Error retriving geoquery", Toast.LENGTH_SHORT).show();
                }
            });

        }

Here's the error:

java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference

Please let me know how to retrieve only those posts which are within 0.5 kms of the user?

Zoe
  • 27,060
  • 21
  • 118
  • 148
Hammad Nasir
  • 2,889
  • 7
  • 52
  • 133

3 Answers3

2

There is a way to find matches in Geo Query in Java also.. The code is very much similar to UI. Please look in the below link for working code snippet. https://youtu.be/M9d526OyG54 Searching for Driver:: 15th tutorial in Series.

1

You just need to change the second parameter of queryAtLocation()

double radius = 0.5; // in km
geoQuery = geoFire.queryAtLocation(new GeoLocation(currentLatDouble, currentLngDouble), radius);

Are you sure that all items which are over 0.5km from the user are retrieved? Here's how to validate them

Location userLocation = new Location("userLocation");
userLocation.setLatitude(currentLatDouble);
userLocation.setLongitude(currentLngDouble);
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
    @Override
    public void onKeyEntered(String key, GeoLocation location) {
        Location itemLocation = new Location("itemLocation");
        itemLocation.setLatitude(location.latitude);
        itemLocation.setLongitude(location.longitude);
        Log.d("distance to " + key" is", userLocation.distanceTo(itemLocation) + "");
    }
Wilik
  • 7,630
  • 3
  • 29
  • 35
  • i am sorry, I forgot to change that parameter. It was set as 0.5 in my original code. It is still not retrieving! – Hammad Nasir Jul 15 '16 at 06:54
  • are you sure that all items which are over 0.5km from the user are retrieved? look at my updated answer for how to validate them – Wilik Jul 15 '16 at 07:08
  • No, bro. I'm unable to achieve what I want. I am writing a problem. Can you please code for it from scratch? The problem is that "suppose I saw a rare species at some place. I uploaded about it along with it's current location coordinates. Now what I want is I want that only those user's who are within 0.5 kms radius of the spot where that rare species was seen should see that post posted by me. Not every user of the app, just those who are within 0.5 kms of that spot." I hope you got my question. Please edit your answer with a code for solving above described problem. Thanks! :) – Hammad Nasir Jul 16 '16 at 00:26
  • I'm not sure what's your problem here, I have been using GeoFire for my projects and it works fine. Based on your example, to save the rare species location, you can call `geoFire.setLocation()`, to get the rare species just call `geoFire.queryAtLocation()`. The first parameter of `queryAtLocation` method is your user's location, so you need to get their location using location provider, the second parameter is the radius in km. GeoFire will return only the places which are in the radius range from the user's location provided from the first parameter. – Wilik Jul 16 '16 at 18:20
  • I hope you understand how GeoFire works. and btw you need to explain your downvote here. – Wilik Jul 16 '16 at 18:21
  • Looks like you have to read more about [geofire](https://github.com/firebase/geofire-java) especially the **Geo Queries** events. Let me explain, GeoFire returns the place one by one on the `onKeyEntered` method, and you just need to do `ref.child(key).addListenerForSingleValueEvent()` inside that method. You DON'T need to get ALL of the requests like what you do right now, that makes geofire useless. Hope this clears everything. – Wilik Jul 16 '16 at 18:48
  • thanks for your reply bro and sorry for the downvote. I edited the code, please see the edited question. I am getting a new error and I have mentioned there. Please take a look and also edit your answer so that I can upvote it again. – Hammad Nasir Jul 17 '16 at 07:25
  • 1
    I think you need to create a new question for the error, that's out of topic already. Has it works for you "retrieve posts within 0.5km" ? You'll have to explain what's your real problem and simplify your code, you code is quite confusing. [how to ask a good question](http://stackoverflow.com/help/how-to-ask) – Wilik Jul 17 '16 at 09:45
  • Here's the link to the new question: http://stackoverflow.com/questions/38411043/geofire-returning-keys-outside-of-range Please have a look! Thanks. – Hammad Nasir Jul 17 '16 at 13:03
  • Here, I have modified the question correctly: http://stackoverflow.com/questions/38427327/getting-int-java-lang-string-length-on-a-null-object-reference-when-trying-t Please answer it.. – Hammad Nasir Jul 18 '16 at 01:29
1

Not knowing much about this I can't help much, however looking at your code this extract, from the GeoFire 2.0 page here, gives a good outline in javascript (rather than java) of what you're looking for.

GeoFire’s real utility is shown by creating a GeoQuery. Say you are making an app for cyclists which shows the bicycle shops within a mile (1.609 kilometers) of a user’s current location. The first thing you need to do is add the bike shops you care about to GeoFire. Then, create a query centered at the user’s current location (let’s say they are at [37.4, -122.6]):

var geoQuery = geoFire.query({
  center: [37.4, -122.6],
  radius: 1.609 //kilometers
});

A query on its own is not very useful. However, GeoFire allows you to add callback functions which are fired when important query events happen. Since you want to display every bike shop which matches the query criteria, listen for the key_entered event. Every time a key (that is, a bike shop) enters the query, the callback you defined will get called with data about that location:

geoQuery.on("key_entered", function(key, location, distance) {
  console.log("Bicycle shop " + key + " found at " + location + " (" + distance + " km away)");
});

It is important to realize that the key_entered event works like typical Firebase events. That is, it will be fired for every key in GeoFire which matches the query criteria, both keys which are already in GeoFire and those which are added at any point in the future. Thanks to Firebase, you receive these events in realtime.

GeoFire is smart about how it looks for keys within the query. It does not need to load all of the GeoFire data into memory. If your user is looking for bicycle shops in San Francisco, GeoFire will not load data for locations in New York only to realize that they are on the opposite side of the country. It only checks on locations which are actually nearby. This keeps your app light and responsive, regardless of how big your data set is.

If your user is biking around while looking for a shop to visit, it is possible that shops which were within a mile of them are now further away. To deal with this, GeoFire will fire the key_exited event for each key which leaves the query:

geoQuery.on("key_exited", function(key, location, distance) {
  console.log("Bicycle shop " + key + " left query to " + location + " (" + distance + " km away)");
});

That is really all you need to make your app work.

The details available for Java appear to be sparser but for what you are trying to do this appears to be the thing:

GeoQuery geoQuery = geoFire.queryAtLocation(currentUserLocation, 1.6);

Reference here.

  • your answer has code in `javascript`. can you please let me know about accurate `java` code for this? – Hammad Nasir Jul 15 '16 at 07:04
  • I hope what I have added is of help to you, there appears to be less focus on Java, it may just be I haven't looked in the right place. – Malachi Prior Jul 15 '16 at 07:29
  • Firebase geofire-js : how to get list of keys of near by static(fixed) locations : http://stackoverflow.com/questions/43632746/firebase-geofire-js-how-to-get-list-of-keys-of-near-by-staticfixed-locations – Ankit Maheshwari Apr 26 '17 at 11:56