Skip to content

Instantly share code, notes, and snippets.

@joeyoun9
Last active August 31, 2017 15:56
Show Gist options
  • Save joeyoun9/431b0b3e554234e4c7dd to your computer and use it in GitHub Desktop.
Save joeyoun9/431b0b3e554234e4c7dd to your computer and use it in GitHub Desktop.
API Station Display

Synoptic Station Display

A simple display of the closest observation to a user using the Synoptic V2 data API.

How this works

This simple app uses a user's location to grab the nearest Synoptic station, and then displays the most recent observation from that station. The user doesn't have to (or get to) customize anyhing. This app uses jquery for JavaScript functions, and a small bit of bootstrap formatting.

Procedure

  • Acquire their location using ipinfo.io's API
  • Use a /stations/metadata query to download all the nearby stations
  • Look through the list, and grab the closest
  • Use the /stations/nearesttime service to download the latest observation from that station
  • display the information to the user

In the script, the functions are cascaded, since they rely on one being completed before the next, so the order is

  1. find_me() to use the ipinfo.io service to get the user's approximate location
  2. get_nearest_stations() to get the Synoptic stations nearby
  3. eval_radial_stations() picks the closest station to you from the list of stations grabbed from the API
  4. get_latest_ob() will download the most recent observation from the selected station
  5. and display_station_latest() will take the latest data from the API and put it into the HTML for display.

All these functions use jQuery selectors and JSONP download procedures to bring data into the user's web interface.

Produced 16 April 2015

Joe Young

Synoptic Data Corp.

<!DOCTYPE HTML>
<!-- Bootstrap, because why not. -->
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
<style>
/*
* Define a couple styles to sit on top of bootstrap and color/size tables,
* temperatures, and winds properly
*/
body {text-align:center;}
.lighter { color:#999; }
.lgoo {color:#009;}
.middle-tbl {width:30%;margin:auto;min-width:400px;text-align: center;}
.middle-tbl td {width:50%;}
.bigdisp {font-size:6em;font-weight:600;line-height:.8em;}
.bigdisp small {font-size:.3em;color:#999;font-weight:300;}
.meddisp {font-size:4em;font-weight:600;line-height:.7em;}
.meddisp small {font-size:.3em;color:#999;font-weight:300;}
.temp { color: #900;}
.dewp { color: #090;}
</style>
<script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
<script>
/*
* Declare some variables
*/
var stationID = "";
var myLocation = "";
/*
* Declare some constants
*/
const WDDIRS = ['N','NNE','NE','ENE','E','ESE','SE','SSE',
'S','SSW','SW','WSW','W','WNW','NW','NNW','NW','N']
const API_TOKEN = "demotoken";
const API_SOURCE = "https://api.synoptic.io/v2/";
/*
* Use jQuery to execute the script once all scripts are loaded
*/
$(find_me);
/*
* Procuction methods - the functions which do the work.
*/
function find_me() {
/*
*
* Here we are using an open service from ipinfo.io to locate the
* user via their IP address. There are many such services on the
* web, and without paying for them, you can only get a limited
* number of requests and/or features. Commercial application of
* these web services are also restricted, so chek the terms before
* you employ this technique in an actual application. See the terms
* for more information: http://ipinfo.io/developers#terms
*
* THIS IS NOT A SERVICE OF THE SYNOPTIC API, and is not affiliated
* with the Synoptic Data Corporation.
*/
$.getJSON('https://ipinfo.io/json/?callback=?',function(location) {
myLocation = location.loc;
get_nearest_stations();
});
}
function get_nearest_stations() {
/*
* Download the metadata for the stations within a 20 mile radius of
* your location. Then call eval_radial_stations with the downloaded
* list.
*
* We are using the cache_query function defined below to take
* advantage of browser caching for this query, if available.
*/
get_latest_ob();
return;
cache_query(API_SOURCE+"stations/metadata?callback=?",{
token:API_TOKEN, // define the token to be used
// in the queries
status:"active", // only get active stations
radius:myLocation+",20", // a radius query to specify
// what stations to download
limit: 1,
vars: "air_temp,wind_speed,wind_direction", // restrict to stations that
// have either of these
// variables (preferably both)
varoperator:'and',
complete:1, // download a full metadata
// report for the resulting
// stations
}, eval_radial_stations );
}
function eval_radial_stations(stationData) {
/*
* Take download output from the API with a list of stations, and
* figure out which one is best
* utilizing the included 'variable' attribute (which in a radius query
* is the computed GC distance from the point)
*
* once a station is determined, call get_obs to download the latest
* obs from this station.
*/
// First, test if there are any results at all, if none, then report
// that there are no stations available.
if (stationData.STATION.length == 0) {
show_no_stations();
return;
// this ends the process, since we do not call the subsequent
// functions after displaying this information.
}
/*
* Loop through the list and find the closest station to the location
* provided.
*/
// define an evaluation variable so we can hold the station id of the
// closest station as we loop [id, distance]
var pickedStid = ["",21];
var s;
for (s in stationData.STATION) {
var stn = stationData.STATION[s];
// perform the distance test using stn.variable, the added range
// calculation
if (stn.variable < pickedStid[1]) {
// the distance is smaller, so set this station to the "Closest"
// for now
pickedStid = [stn.STID, stationData.STATION[s].variable]
}
}
/*
* Once the loop is done, the final value in our placeholder is the
* nearest, so set it, and get the data
*/
window.stationID = pickedStid[0];
get_latest_ob();
return;
}
function get_latest_ob() {
/*
* Download the nearesttime observation from the station we have selected.
*
* Once downloaded, pass the data to display_station_latest so it can
* be shown.
*/
$.getJSON(API_SOURCE+"stations/nearesttime?callback=?",{
token:API_TOKEN, // our API token must be passed
// with all queries
radius:myLocation+",20", // a radius query to specify
// what stations to download
limit: 1,
vars: "air_temp,wind_speed,wind_direction", // restrict to stations that
// have either of these
// variables (preferably both)
varoperator:'and',
within:250, // We will accept an ob as long as
// it is within the last 250 minutes
units:"english", // We request the data in common
// American units for display
}, display_station_latest );
return;
}
function display_station_latest(data){
/*
* Take the download API data object, and display it in the HTML
* elements we defined on the page
*/
if (!("STATION" in data) || data.STATION.length == 0) {
// Though we selected a valid station via the processes above, we
// ultimately received no data. this is usually an API error.
$(".js-stid").html("Strange, station "+stationID+" not found!");
return;
}
/*
* Ther are stations, so we grab the first one of the list (we should
* have only requested one anyway) so we can access it more easily.
*/
var stn = data.STATION[0];
/*
* Use jquery selectors to populate the different elements with the
* observation values from the station.
*/
// station information
$(".js-stid").html(stn.STID);
$(".js-stname").html(stn.NAME);
// displayed observation time
$(".js-time").html("at "+
(new Date( stn.OBSERVATIONS.air_temp_value_1.date_time ))
.toLocaleString());
// Temperature
$(".js-temp").html(Math.round(stn.OBSERVATIONS.air_temp_value_1.value)+
"<small>&deg;F</small>");
// Moisture - which can be in one of two froms. RH is preferred, because
// it is usually what is actually measured (but not always!)
if (data.UNITS.relative_humidity) {
$(".js-moisture-var").html("Relative Humidity")
$(".js-mois").html(stn.OBSERVATIONS.relative_humidity_value_1.value+
"<small>%</small>");
} else if (data.UNITS.dew_point_temperature) {
// in this case, the station reported no RH, but did report a dew
// point, so we plot that.
$(".js-moisture-var").html("Dew Point")
$(".js-mois").html(
stn.OBSERVATIONS.dew_point_temperature_value_1.value+
"<small>&deg F</small>");
}
// Wind direction - display textually using the WDDIRS constant
var wdix = Math.round(
WDDIRS.length * stn.OBSERVATIONS.wind_direction_value_1.value/360);
var wddir = WDDIRS[wdix];
$(".js-wind-dir").html(wddir+" <small>("+
stn.OBSERVATIONS.wind_direction_value_1.value+"&deg;)</small>");
// Wind Speed
$(".js-wind-spd").html(Math.round(stn.OBSERVATIONS.wind_speed_value_1.value)+
"<small>kts</small>");
/*
* And thus the data are displayed. Again, there is no user interactive
* aspect to this service, so we are done.
*/
return;
}
/*
* Utility functions
*/
function show_no_stations() {
/*
* This gets called if there are no stations found
*
* display a message with the city/state/country they are in if there
* are no stations available.
*/
var loc = "";
// if there is a city, then add that
if (myLocation.city) loc += myLocation.city+", ";
// same for a state and region
if (myLocation.region) loc+= myLocation.region+", ";
// if neither are available, then put in a question mark to show that we tried
if (loc == "") loc = "?, ";
// and add the country, which is almost always available.
loc += myLocation.country;
// and then display in the interface
$(".js-stid")
.html("Sorry, we have no observations near <span class='lighter'>"+
loc+"</span>");
}
function cache_query(url, data, result) {
/*
* This is a simple utility function to override jquery anti-cache
* efforts for certain JSONP requests
*/
$.ajax({
url:url,data:data,
dataType: 'jsonp',
cache:true,
crossDomain:true
}).done(result);
return;
}
</script>
<h4>My Weather from <a href='http://synoptic.io' class='logo'>Synoptic</a></h4>
<h2>
<span class='js-stid'>Loading...</span>
<span class='js-stname lighter'></span>
</h2>
<h4 class='js-time'></h4>
<table class='middle-tbl'>
<tr>
<td>
<h4>Temperature</h4>
<div class='js-temp bigdisp temp'></div>
<h5 class='js-moisture-var'></h5>
<div class='js-mois meddisp dewp'></div>
</td><td>
<h4>Wind</h4>
<div class='js-wind-spd bigdisp wind'></div>
<h5>from the</h5>
<div class='js-wind-dir meddisp wind'></div>
</td>
</tr>
</table>
<table class='js-tabular'>
</table>
<small>Observations sourced from Synoptic's version 2 data API</small>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment