|
<!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>°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>° 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+"°)</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> |
|
|
|
|
|
|
|
|
|
|