angular geofire query

1,304 views
Skip to first unread message

Bosch

unread,
Aug 5, 2014, 4:18:47 AM8/5/14
to fireba...@googlegroups.com
Hey Guys, here's a trivial problem from a newbie (I just started messing with this trio of angular/firebase/geofire and greatly enjoy it).

I have this setup of Firebase items (textual data) with corresponding Geofire locations by item Key as suggested in this group. 
In my singlepage app (angular) I have a model bound to this Firebase ref and an iteration through these items (i.e. ng-repeat "r in restaurants") which works quite well: they all show up with details, ordered by distance, a new item pops out whenever someone adds a new one, etc.

What I want is to only show the "restaurants" within a specific range specified by the user (i.e. 300m from his location), I don't care if a new one suddenly pops out so I don't need a geoquery event (right now this is solved by firebase but it's not so important), just a simple query by radius/location. The user should be able to change the radius and more "restaurants" will be included in his result set. I don't want to filter by distance from the model data, just include in the model data only the corresponding items by location/radius.

How can I accomplish that with my current setup? How can I set up a geo query that will filter my angular model data by a custom radius? Or even simpler: how can i bind a geoquery to the angular model which is bound to firebase?

Any suggestion will be appreciated. A short example would be even more helpful as I don't fluently speak angular/fb/gf  :)


Thanks in advance, 

Bosch

Jacob Wenger

unread,
Aug 5, 2014, 2:42:26 PM8/5/14
to fireba...@googlegroups.com
Hey Bosch,

Glad you are a fan of GeoFire and Firebase! I'm not entirely 100% sure what you want to do. If I understand correctly, you want to take a set of GeoFire locations and get a list of those locations which are within a certain radius of another location. But you don't want this to update in realtime. Instead, you just want to do this once. You can definitely accomplish this by making a GeoQuery, keeping a list of every location which enters that query, and then turning off the query once the ready event has fired. It would look like this:

// Create a new GeoFire reference
var ref = new Firebase("https://<your-firebase>.firebaseio.com/");
var geoFire = new GeoFire(ref);

// Define our query criteria
var geoQuery = geoFire.query({
  center: [lat, lon],
  radius: 0.3
});

// Keep track of the locations which meet our criteria
$scope.filteredLocations = [];

// Add every key within our query to our locations list
geoQuery.on("key_entered", function(key, location, distance) {
  filteredLocations.push(key);
});

// Cancel the query once all data has loaded
geoQuery.on("ready", function() {
  geoQuery.cancel();
});

You now have the list of locations you want in $scope.filteredLocations. If you update the query (e.g. make the radius larger), just make a new GeoQuery and repeat the code above. I'm not sure exactly why you don't want the query to update in realtime, but if you did, you can just add a key_exited event listener to remove those keys from $scope.filteredLocations. That way, you can also update the query criteria on your existing GeoQuery without having to create a new GeoQuery.

Once you have $scope.filteredLocations, you can use the keys it contains to access data for locations in, say, a /locations/ node somewhere in your Firebase. That /locations/ node would have children who keys are the same keys you used in GeoFire. So you could look up the location data by doing something like ref.child("locations").child(key_from_filtered_locations).once("value").

I'm not sure if you are using this or not, but Mike Pugh made an Angular service which wraps our GeoFire library. It's called AngularGeoFire and you should check it out if you aren't already using it.

Finally, if you are using Firebase and Angular, you should check out AngularFire. It should play nicely with AngularGeoFire.

Good luck!
Jacob


--
You received this message because you are subscribed to the Google Groups "Firebase Google Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to firebase-tal...@googlegroups.com.
To post to this group, send email to fireba...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

uNki

unread,
Aug 15, 2014, 7:35:49 AM8/15/14
to fireba...@googlegroups.com
very interesting topic, i'm also struggeling with that one..

using parse.com for example, you can make "nearby-queries" using coordinates. https://parse.com/docs/js_guide#geo-classes
let's say you have an array of restaurants, each one with latitude and longitude property. you also have your current position. parse.com lets you search for restaurants that are within a radius of 10km of your current position.

does firebase support something like this? one could do that easily on the client-side using custom filters. but i don't want to filter the results on the client-side to reduce traffic. i want to get only those records from the database that match the "within 10km radius" criteria.

according to it's name, GeoFire seems do be able to do that. is there any example how?

thanks and best regards!

Jacob Wenger

unread,
Aug 15, 2014, 5:18:04 PM8/15/14
to fireba...@googlegroups.com
Hey there,

I kind of answered this on my response on the other thread you started, but I wanted to answer here as well. GeoFire will only selectively load the data that it needs to and makes sure that you don't read data from the database that you don't have to. Although all the code you are writing is client side, the filtering is done using priorities, which allows our Firebase backend to only send you a subset of the data. I think you are worried about an issue that simply does not exist.

As for examples, I have created many of them on GeoFire's GitHub repo. The most fully-featured example is the SF vehicles on. You can check out the demo and see what is possible here.

Jacob


uNki

unread,
Aug 16, 2014, 5:19:15 AM8/16/14
to fireba...@googlegroups.com
hello jacob,

thank you for your explanation! i was mainly afraid that geofire would just load all the data and process it on the client side. since you clarified that it does the filtering on the server side i think thats fine!

your examples are great! i'm just missing a minimalistic example of a nearby search - i'm wondering if that isn't a typical usecase? it's unclear to me how to structure the data in my firebase backend so that geoquery can work with it. 

is it like that:

  • stores
    • 1
      • "lat" : "51.8792"
      • "long" : "40.5839"
      • "name" : "Michael's Sportsstore"
    • 2
      • "lat" : "51.4483"
      • ...
    • ...

or something like that:

  • data
    • coordinates
      • "51.5392,50.4792" : "1"
      • "50.7372,50.3820" : "2"
    • stores
      • 1
        • "name" : "Michael's Storesstore"
      • 2
        • "name" : "Some other store"

unfortunately, thats absoluteley not clear to me :)

thank you very much!


On Tuesday, August 5, 2014 10:18:47 AM UTC+2, Bosch wrote:

Jacob Wenger

unread,
Aug 16, 2014, 3:00:02 PM8/16/14
to fireba...@googlegroups.com
Hey there,

The good news is that this is easier than you are making it. Let's walk through a very simple example. Say you have a list of restaurants which you created by calling Firebase's push() API:

{
  "restaurants": {
    "-J92D8d123ab42": {
      "name": "Jacob's Diner",
      "city": "San Francisco",
      "state": "California",
      ...  
    },
    "-J929IqW2bqb0i": {
      "name": "Cheesecake Factory",
      "city": "Palo Alto",
      "state": "California",
      ...
    },
    "-J8jp)3J5a69m7": { ... },
    ...
  }
}

Now, to use these will GeoFire, we first need to create a GeoFire object. Let's make it at a geofire node at the root of our Firebase:

var firebaseRef = new Firebase("https://<your-firebase>.firebaseio.com");
var geoFire = new GeoFire(firebaseRef.child("geofire"));

Then we can start adding restaurants to GeoFire by using GeoFire's set() method:

geoFire.set("-J92D8d123ab42", [-127.52, 28.57]).then(function() {
  console.log("Location added to GeoFire");
}).catch(function(error) {
  console.log("Error adding to GeoFire:", error);
});

You can do this for all locations in your /restaurants/ node. Note that I'm using each restaurant's push ID as its corresponding GeoFire key. This is because when we want to do a GeoQuery, we will get back this node and can easily look up each restaurant. Here is what that looks like:

var geoQuery = geoFire.query({
  center: [-126, 30],
  radius: 10
});

geoQuery.on("key_entered", function(key, location, distance) {
  // Use the "key" to look up the corresponding restaurant
  firebaseRef.child("restaurants").child(key).once("value", function(snapshot) {
    var restaurantData = snapshot.val();
    console.log("Found a restaurant: " + restaurantData.name);
  });
});

So instead of formatting the data yourself to make it work with GeoFire, let us handle the work and format it for you. Just use set() and remove() to add and remove locations from GeoFire. Then look them up by the key returned from the GeoQuery callbacks.

Make sure you read through the full GeoFire documentation here. It has some good stuff in there and should be enough for you to get up-and-running.

Good luck!
Jacob


uNki

unread,
Aug 16, 2014, 8:31:51 PM8/16/14
to fireba...@googlegroups.com
thanks jacob, that helped alot!

i thought that geofire would directly query the location data from my restaurant objects and that those have to be applied in a special format. but it creates it's own geohash data actually - i have to set those keys for each location. now it's clear to me. and now it's also clear to me why those searches are happening on the server side.

thank you! 


On Tuesday, August 5, 2014 10:18:47 AM UTC+2, Bosch wrote:

Andre

unread,
Dec 20, 2014, 3:48:42 PM12/20/14
to fireba...@googlegroups.com
Hey Jacob,
Thank you for Geofire and all these explanations. 
In the case of SF Vehicles js, I do not understand how geofire firebase is populated from publicdata firebase, and where _geofire is stored,if it is.

Thanks for help.

André

Jacob Wenger

unread,
Dec 26, 2014, 10:54:47 AM12/26/14
to fireba...@googlegroups.com
Hey Andre,

In the future, please start a new Google Groups thread with your question instead of bumping an existing thread.

Firebase provides a collection of data sets called Firebase Open Data Sets. For the SF vehicles demo, I used our Transit Open Data Set. This data set is kept up-to-date by us here at Firebase and is free to use. You can view the Firebase Dashboard for this Firebase at https://publicdata-transit.firebaseio.com. The _geofire node you are asking about is the GoeFire index for all the vehicles in the Transit Open Data Set, and it can be found here.

In the SF vehicles demo, you can see we create a Firebase reference to our Transit Open Data Set (code). We also create a reference to the _geofire node of that Firebase (code). We then listen for new vehicles and add them to the map (code).

Good luck!
Jacob

--
You received this message because you are subscribed to the Google Groups "Firebase Google Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to firebase-tal...@googlegroups.com.
To post to this group, send email to fireba...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages