It looks like you have a very nice use-case, but I won't try to translate it into a working application for you. Instead I'll provide some pointers to the most important parts, where they relate to Firebase.
Data structure
Say you use this structure for your points of interest:
locations
-123981234cbdag
title: "White House"
latitude: 38.897676
longitude: -77.036528
-458614tajsla12
title: "Google San Francisco"
latitude: 37.789635
longitude: -122.390375
The names/keys of the locations in this snippet ("-123981234cbdag" and "-458614tajsla12") are of the format that Firebase generates when you call its push
method.
Retrieving data from Firebase in a specific order
Firebase can provide the locations to your application in any order:
- by their key (which is also chronological when you use
push
)
- by their priority (which is not showing in the example above)
- by the value of any child property
Code for each:
// we call this helper function below to print the locations
function printSnapshot(locations) {
locations.forEach(function(snapshot) {
console.log(snapshot.val().title);
});
}
var ref = new Firebase('https://your.firebaseio.com/locations');
// 1a. gives a snapshot with the children in order of their key
ref.on('value', printSnapshot);
// 1b. the same result, but explicitly orders the keys in order of their name
ref.orderByKey().on('value', printSnapshot);
// 2. gives a snapshot with the children in the order of their priority
ref.orderByPriority().on('value', printSnapshot);
// 3a. gives a snapshot with the children in the order of their title
ref.orderByChild('title').on('value', printSnapshot);
// 3b. gives a snapshot with the children in the order of their latitude
ref.orderByChild('latitude').on('value', printSnapshot);
// 3c. gives a snapshot with the children in the order of their longitude
ref.orderByChild('longitude').on('value', printSnapshot);
Once you've ordered the data, you can also limit what is retrieved.
Retrieving fewer locations that are available
This snippet will just return the White House:
ref.orderByChild('title').equalTo('White House').on('value', printSnapshot);
So will this snippet:
ref.orderByChild('title').limitToLast(1).on('value', printSnapshot);
But this last might return a different location once one of your users visits the Zion National Park. Also of note is that this last snippet will automatically receive any new value that might show up in your Firebase, since Firebase actively synchronizes data that fits your query.
So the flow might be:
- your application is started
- it runs the above query, which asks for the last location when ordered by title
- Firebase sends the White House location to your application, which your application displays
- somebody visits Zion National Park and inserts that data into Firebase
- Firebase sends the Zion National Park location to your application
- etc
Filtering by latitude or longitude
OK, we got distracted a bit here. Let's get back to your use-case. I'll assume you know the user's location, but have a look at this question if you don't.
var whereAmI = { latitude: 38.898042, longitude: -77.03326};
If somebody added exactly the location where this user is, we we can use equalTo
like we did before. But given the varying precision of GPS devices out there, this will not be very reliable. So instead we should check for locations that are close by, so that are within a certain range of our latitude/longitude.
We can use Firebase's startAt
and endAt
methods for that:
ref
.orderByChild('latitude')
.startAt(whereAmI.latitude - 0.002)
.endAt(whereAmI.latitude + 0.002)
.on('value', printSnapshot);
This snippet will return the locations that are within a few hundred meters east or west of where you are, which... guess what... is just the White House in our sample data set!
If we'd tack on a limitToFirst(1)
it would even limit the number of locations returned. But unfortunately it would return the eastern-most location, which is not necessarily the one that is closest to me. (I'm not terribly good with latitudes, it might also be the western-most location) Besides, it only caters for latitude; you also want to use longitude to find locations that are just north or south of you.
Ideally you would be able to do something like this in Firebase:
// !!!THIS IS A NON-WORKING EXAMPLE!!!
ref
.orderByChild('latitude')
.startAt(whereAmI.latitude - 0.002)
.endAt(whereAmI.latitude + 0.002)
.orderByChild('longitude')
.startAt(whereAmI.longitude- 0.002)
.endAt(whereAmI.longitude+ 0.002)
.on('value', printSnapshot);
// !!!THIS IS A NON-WORKING EXAMPLE!!!
But unfortunately Firebase can currently only filter by one child per query. So either we have to retrieve all children in an entire latitude slice of the earth and filter client-side or we have to find a way to stuff latitude and longitude into a single property that also maintains the nice "it can easily be ordered" properties of numbers.
Enter "Geohashes" and GeoFire.
Filtering by Geohash (using Geofire)
If you go to Firebase's demo of real-time public transport information, you'll see something pretty close to your use-case. It shows all MUNI (the San Francisco public traffic) lines in a certain area.

Screenshot from mobile, because I can't find one from the desktop interface (and it would look very similar anyway)
If you wait a few seconds (and are visiting during operating hours), you'll even see the vehicles move around. And the area they show is a circle, so clearly whoever wrote that demo figured out how to query both latitude and longitude.
The trick they use is called Geohashing and to quote Wikipedia:
It is a hierarchical spatial data structure which subdivides space into buckets of grid shape.
Geohashes offer properties like arbitrary precision and the possibility of gradually removing characters from the end of the code to reduce its size (and gradually lose precision).
As a consequence of the gradual precision degradation, nearby places will often (but not always) present similar prefixes. The longer a shared prefix is, the closer the two places are.
This reads like the secret sauce for an application like yours: a geohash encodes latitude and longitude into a single value, which can be ordered. I recommend playing a bit with geohashes. Since the original geohash.org seems to be down, I used the Geohash service of openlocation.org. It even starts around the White House and shows that our location above is somewhere around these Geohashes: ('dqcm0n','dqcm0h','dqcm0m','dqcjpv','dqcjpy','dqcm0q','dqcm0k','dqcjpu','dqcm0j')
The guys at Firebase also realized that this would be pretty useful, so they created a helper library called Geofire. There is a great introduction and tutorial for GeoFire on Firebase's web site. I suggest you continue working from there.