2

Context

I've used Geofire along with Firebase for a while now and I got really curious how Geofire performs its queries. I understand that semantically, it's a function of coordinates and a radius that results in a minimum and maximum hashes. So the way I think it works in conjuncture with Firebase would look like this

ref.child("users").orderByChild("g").startAt(minHash).endAt(maxHash).on('child_added', function(snapshot) { /* retrieved snapshot contains the geohashes in range */ });

Where these two (min and max) geohashes are calculated off the given inputs. Now here comes the question

Question(s)

Assuming that what I stated above is correct, how are these two geohashes calculated? How do they return results within a certain circular region when geohashes typically represent bounding rectangles? And finally, how can two geohashes of different sizes have the same center?

To clarify on that last part: consider the following image

Typical geohashing steps

Since geohashing works by halving regions into smaller regions, how can two hashes of different sizes (min and max) have the same center points?

Assumptions

I thought that maybe it's as simple as increasing/decreasing the raw value of the hash, but that doesn't make much sense since the increase/decrease should be relative to the size of the hash (the "zoom" level so to speak) and the query radius, if I'm not mistaken.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
ThunderStruct
  • 1,504
  • 6
  • 23
  • 32

1 Answers1

3

GeoFire actually performs a range query of a rectangular region against the database. This range is the smallest rectangle that encompasses the range indicated in the query.

It then in the client-side code checks the actual distance of each key to the center of the query and only fires the key_entered/key_moved event for items inside the query.

The relevant code is here:

// Determine if the location is within this query
distanceFromCenter = GeoFire.distance(location, _center);
isInQuery = (distanceFromCenter <= _radius);

...

// Fire the "key_entered" event if the provided key has entered this query
if (isInQuery && !wasInQuery) {
  _fireCallbacksForKey("key_entered", key, location, distanceFromCenter);
} else if (isInQuery && oldLocation !== null && (location[0] !== oldLocation[0] || location[1] !== oldLocation[1])) {
  _fireCallbacksForKey("key_moved", key, location, distanceFromCenter);
} else if (!isInQuery && wasInQuery) {
  _fireCallbacksForKey("key_exited", key, location, distanceFromCenter);
}
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807