<x-discover> - Geo Location based custom Polymer element
Older Article
This article was published 12 years ago. Some information may be outdated or no longer applicable.
After building the
It extends Eric Bidelman’s geo-location with a bunch of extra features bolted on.
So what does it do? It grabs the latitude and longitude pair using navigator.geolocation, then runs a geo lookup to figure out where the user actually is. That lookup hits the Yahoo Geo API. But it doesn’t stop there. Using the same lat/long pair, it fires another query at the FourSquare API to pull the top 10 attractions nearby. Hence the name “x-discover.”
I’m still wiring up features, but I’m also using navigator.geolocation.watchPosition() so the location and attractions list update automatically as the user moves. Querying two third-party APIs on every position change would be expensive though, so I’ve implemented the haversine formula to measure the distance between the starting point and the user’s current position. Right now the user needs to travel 100 units (an angular distance between two points) before it triggers a refresh.
<polymer-jsonp
id="location"
on-polymer-response="{{getLocationName}}"
url="http://query.yahooapis.com/v1/public/yql?q=select%20neighborhood,city,countrycode%20from%20geo.placefinder%20where%20text%3D%22{{latitude}}%2C{{longitude}}%22%20and%20gflags%3D%22R%22&format=json&callback="
></polymer-jsonp>
<core-ajax
id="poi"
handleAs="json"
on-core-response="{{getpoi}}"
url="https://api.foursquare.com/v2/venues/explore?client_id={{clientid}}&client_secret={{clientsecret}}&v=20130815&ll={{latitude}},{{longitude}}&query=outdoor&limit=10&venuePhotos=1"
></core-ajax>
onPosition: function(pos) { //gets current position
var radius = 6371; // Earth's radius in km
if (init) {
this.latitude = pos.coords.latitude;
this.longitude = pos.coords.longitude;
this.$.location.go();
init = false;
}
if (distance > 100) { //limit the querying
this.latitude = pos.coords.latitude;
this.longitude = pos.coords.longitude;
this.$.location.go();
}
//calculate the distance using the haversine formula
var actualLatitude = pos.coords.latitude,//now
storedLatitude = this.latitude, //stored
actualLongitude = pos.coords.longitude, //now
storedLongitude = this.longitude, //stored
deltaLatitude = storedLatitude - actualLatitude, //distance between lats
deltaLongitude = storedLongitude - actualLongitude, //distance between longs
a = Math.sin(deltaLatitude/2) * Math.sin(deltaLatitude / 2) + Math.cos(actualLatitude) * Math.cos(storedLatitude) * Math.sin(deltaLongitude/2) * Math.sin(deltaLongitude/2),
c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); //final formula
distance = radius * c; //distance between two points
distance = Number(distance.toFixed(0)); //distance trick with toFixed();
}
I’ve also added another throttle: comparing the neighbourhood value returned by the Yahoo Geo API between queries. If the neighbourhood hasn’t changed, the app skips the FourSquare call entirely.
getLocationName: function(e, r) { //gets city and countrycode
if (this.neighborhood !== r.response.query.results.Result.neighborhood) { //only collect POI when neighborhood changes
this.$.poi.go();
}
this.neighborhood = r.response.query.results.Result.neighborhood;
this.city = r.response.query.results.Result.city;
this.country = r.response.query.results.Result.countrycode;
this.location = 'You are around: ' + this.neighborhood + ' - ' + this.city + ', ' + this.country;
},
Here’s a teaser screenshot. The <template> portion of the element is still rough (not much styling work done yet, as you can see).
I’d like to extend this with features like detecting whether someone is walking or driving and adjusting refresh intervals accordingly. I also want to let users override the lat/long from navigator.geolocation, so instead of just <x-discover> you could write <x-discover latitude=[lat] longitude=[lng]></x-discover> and explore a specific location.
The one problem I’m stuck on is the FourSquare API. They require a clientid and client secret for all REST endpoints, which means embedding those in a Polymer element exposes them publicly. Viewing the source of x-discover.html reveals the credentials. I haven’t figured out how to work around that yet, but I’ll keep thinking.
Once I’ve tidied up the code, I’ll push it to GitHub. Stay tuned.