0

I have a model in angularJS which is bound to firebase $scope.items=$firebase(blah) and I use ng-repeat to iterate through the items. Every item in firebase has a corresponding geofire location by the key of the item.

How can I update my controller to only include items by a custom radius around the user? I don't want to filter by distance in angular, just ask firebase to only retrieve closer items (say 0.3km around a location). I looked around geoqueries but they have a different purpose and I don't know how to bind them to the model anyway. The user may change the radius and the items list should be updated accordingly, so they need to be bound somehow.

Any suggestion is welcome, but an example would be greatly appreciated as I don't have fluency in this trio of angular/firebase/geofire yet :P

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
bosch
  • 1,089
  • 11
  • 16
  • Have a look at Geofire, which does exactly what you describe. https://github.com/firebase/geofire – Frank van Puffelen Aug 05 '14 at 11:24
  • like the title says, I am using Geofire :). But I don't know how to bind it to the angular model. Now can I have my points back? :) – bosch Aug 05 '14 at 11:44
  • I have an AngularGeoFire library which should help you - https://github.com/mikepugh/AngularGeoFire. The readme has an example of setting up a GeoQuery and how you'd get that data in your controller (via broadcast listener). – Mike Pugh Aug 05 '14 at 13:50
  • thx @MikePugh, I'll give it a try and come back with an answer. – bosch Aug 05 '14 at 13:59
  • Oops, I missed that you're already using Geofire. I didn't downvote though, but can imagine that people would find the fact that you didn't include any code a reason to downvote. Keep in mind "can you tell my why my code doesn't work?" questions are a lot easier to troubleshoot if they include the relevant code. – Frank van Puffelen Aug 05 '14 at 14:34

1 Answers1

0

It's difficult to figure out what you need to do without seeing your code. But in general you'll need to query a Firebase ref that contains the Geohash as either the name of the child or the priority.

A good example of such a data structure can be found here: https://publicdata-transit.firebaseio.com/_geofire/i

i
  9mgzcy8ewt:lametro:8637: true
  9mgzgvu3hf:lametro:11027: true
  9mgzuq55cc:lametro:11003: true
  9mue7smpb9:nctd:51117: true
  ...
l
  ...
  lametro:11027
    0: 33.737797
    1: -118.294708
  actransit:1006
  actransit:1011
  actransit:1012
  ...

The actual transit verhicles are under the l node. Each of them has an array contains the location of that vehicle as a longitutude and latitude pair.

The i node is an index that maps each vehicle to a Geohash. You can see that the name of each node is built up as <geohash>:<metroarea>:<vehicleid>.

Since the Geohash is at the start of the name, we can filter on Geohash with a Query:

var ref = new Firebase("https://publicdata-transit.firebaseio.com/_geofire");
var query = ref.child('i').startAt(null, '9mgzgvu3ha').endAt(null, '9mgzgvu3hz');
query.once('child_added', function(snapshot) { console.log(snapshot.name()); });

With this query Firebase will give us all nodes whose name falls within the range. If all is well, this will output the name of one node:

  9mgzgvu3hf:lametro:11027

Once you have that node, you can parse the name to extract the vehicleid and then lookup the actual location of the vehicle under l.

Calculating Geohashes based on a location and a range

In the snippet above, I hardcoded the geohash values to use. Normally you'll want to to get all nodes in a certain range around a center. Instead of calculating these yourself, I recommend using the geohashQueries function from GeoFire for that:

var whitehouse = [38.8977, -77.0366];
var rangeInKm = 0.3;
var hashes = geohashQueries(center, radiusInKm*1000);

console.log(JSON.stringify(hashes));

This outputs a number of Geohash ranges:

[["dqcjqch","dqcjqc~"],["dqcjr10","dqcjr1h"],["dqcjqbh","dqcjqb~"],["dqcjr00","dqcjr0h"]]

You can pass each of these Geohash ranges into a Firebase query:

hashes.forEach(function(hash) {
  var query = geoFireRef.child('i').startAt(null, hash[0]).endAt(null, hash[1]);
  query.once('child_added', function(snapshot) { log(snapshot.name()); });
});

I hope this helps you settings things up.

Here is a Fiddle that I created a while ago to experiment with this stuff: http://jsfiddle.net/aF9mN/.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Why would you go to all the trouble of getting geohash ranges from GeoFire and setting up the child_added events instead of just using GeoFire's query method and resulting GeoQuery object? – Mike Pugh Aug 06 '14 at 15:27
  • Mostly because I wanted to understand how you could go about building something like that, without looking at the code. :-) – Frank van Puffelen Aug 06 '14 at 15:38
  • Geohashes sounded a bit like unicorns to me: magical beasts with mythical properties, so I wanted to understand them better. I wrote a Geohash decoder in C# for the same reason: https://github.com/puf/fietsfiles/blob/master/DecodeGeohash.linq. – Frank van Puffelen Aug 06 '14 at 15:55