yr.no-forecast
Advanced tools
Comparing version 0.0.3 to 0.0.4
161
index.js
@@ -24,66 +24,8 @@ // Copyright 2013 Evan Shortiss | ||
// Could do with improving, see schema for api. | ||
var DETAILED_FIELDS = [{ | ||
tagname: 'temperature', | ||
id: 'TTT', | ||
valueSelector: 'value', | ||
name: 'temperature' | ||
}, { | ||
tagname: 'windSpeed', | ||
id: 'ff', | ||
valueSelector: 'mps', | ||
name: 'windSpeed' | ||
}, { | ||
tagname: 'windSpeed', | ||
valueSelector: 'beaufort', | ||
name: 'beaufort', | ||
id: 'ff' | ||
}, { | ||
tagname: 'windDirection', | ||
name: 'windBearing', | ||
id: 'dd', | ||
valueSelector: 'deg' | ||
}, { | ||
tagname: 'cloudiness', | ||
name: 'cloudCover', | ||
id: 'NN', | ||
valueSelector: 'percent' | ||
}, { | ||
tagname: 'humidity', | ||
name: 'humidity', | ||
id: '', | ||
valueSelector: 'value' | ||
}, { | ||
tagname: 'pressure', | ||
name: 'pressure', | ||
id: 'pr', | ||
valueSelector: 'value' | ||
}, { | ||
tagname: 'dewpointTemperature', | ||
id: 'TD', | ||
name: 'dewpointTemperature', | ||
valueSelector: 'value' | ||
}, { | ||
tagname: 'fog', | ||
id: 'FOG', | ||
valueSelector: 'percent', | ||
name: 'fog', | ||
}, { | ||
tagname: 'highClouds', | ||
id: 'HIGH', | ||
name: 'highClouds', | ||
valueSelector: 'percent' | ||
}, { | ||
tagname: 'mediumClouds', | ||
id: 'MEDIUM', | ||
name: 'mediumClouds', | ||
valueSelector: 'percent' | ||
}, { | ||
tagname: 'lowClouds', | ||
id: 'LOW', | ||
name: 'lowClouds', | ||
valueSelector: 'percent' | ||
}]; | ||
/** | ||
* @constructor | ||
* @param {String} xml | ||
* @param {Function} callback | ||
*/ | ||
function LocationForecast(xml, callback) { | ||
@@ -112,6 +54,3 @@ this.xml = xml; | ||
var self = this; | ||
var json = this.json; | ||
var current = json['weatherdata']['product']['time'][0]; | ||
current.symbol = json['weatherdata']['product']['time'][1]['symbol']; | ||
@@ -157,2 +96,3 @@ async.forEach(json['weatherdata']['product']['time'], function(time, cb) { | ||
var curDate = moment.utc().hours(12); | ||
curDate.set('minutes', 0); | ||
@@ -182,3 +122,3 @@ // Create 5 days of dates | ||
} | ||
return callback(null, res); | ||
@@ -204,15 +144,8 @@ }); | ||
if (!basic) { | ||
return callback('No weather basic for time ' + time.toJSON(), null); | ||
return callback(null, {}); | ||
} | ||
var res = { | ||
icon: basic['location']['symbol']['id'], | ||
to: basic['to'], | ||
from: basic['from'], | ||
rain: (basic['location']['precipitation']['value'] + ' ' + basic['location']['precipitation']['unit']) | ||
}; | ||
getDetailForTime(time, self.detail, function(err, detail) { | ||
if (detail) { | ||
return callback(null, buildDetail(detail, res)); | ||
return callback(null, buildDetail(detail, basic)); | ||
} | ||
@@ -241,3 +174,3 @@ return callback(null, res); | ||
function buildDetail(detail, obj) { | ||
function buildDetail(detail, basic) { | ||
// <location altitude="48" latitude="59.3758" longitude="10.7814"> | ||
@@ -257,15 +190,32 @@ // <temperature id="TTT" unit="celcius" value="6.3"/> | ||
var obj = { | ||
icon: basic['location']['symbol']['id'], | ||
to: basic['to'], | ||
from: basic['from'], | ||
rain: (basic['location']['precipitation']['value'] + ' ' + basic['location']['precipitation']['unit']) | ||
}; | ||
// Corresponds to XML 'location' element | ||
var location = detail.location; | ||
var cur = null; | ||
for (var key in location) { | ||
cur = location[key]; | ||
DETAILED_FIELDS.forEach(function(field) { | ||
var item = location[field.tagname]; | ||
// If the field exists, add it and it's value to the object | ||
if (item) { | ||
obj[field.name] = location[field.tagname][field.valueSelector]; | ||
} else { | ||
obj[field.name] = null; | ||
// Based on field type build the response | ||
// Type 1: Has "value" and "unit", combine for result | ||
// Type 2: Has only "percent" | ||
// Type 3: Has multiple properties where the name is the value | ||
if (cur.hasOwnProperty('value')) { | ||
obj[key] = cur['value'] + ' ' + cur['unit']; | ||
} else if (cur.hasOwnProperty('percent')) { | ||
obj[key] = cur['percent'] + '%'; | ||
} else if (typeof cur === 'object') { | ||
obj[key] = {}; | ||
for (var nestedKey in cur) { | ||
if (nestedKey != 'id') { | ||
obj[key][nestedKey] = cur[nestedKey]; | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
@@ -297,2 +247,32 @@ return obj; | ||
/** | ||
* Used when no matching element is found for a time of a day. | ||
* @param {Date} time | ||
* @param {Array} collection | ||
* @param {Function} callback | ||
*/ | ||
function fallbackSelector(time, collection, callback) { | ||
time = moment(time); | ||
// Find out is time before or after collection range | ||
var isBefore = false; | ||
async.each(collection, function(item, cb) { | ||
if(time.isBefore(moment(item.to))) { | ||
isBefore = true; | ||
return cb(true); | ||
} | ||
cb(); | ||
}, function() { | ||
// Take earlist time possbile | ||
if(isBefore == true) { | ||
return callback(null, collection[0]); | ||
} | ||
// Take latest time possible | ||
return callback(null, collection[collection.length-1]); | ||
}); | ||
} | ||
/** | ||
* Get detailed items for a time. | ||
@@ -323,2 +303,6 @@ * Find nearest hour on same day, or no result. | ||
}, function() { | ||
if(res == null) { | ||
return fallbackSelector(date, detail, callback); | ||
} | ||
return callback(null, res); | ||
@@ -329,2 +313,3 @@ }); | ||
/** | ||
@@ -365,2 +350,6 @@ * Get basic items for a time. | ||
}, function() { | ||
if(res == null) { | ||
return fallbackSelector(date, basic, callback); | ||
} | ||
return callback(null, res); | ||
@@ -367,0 +356,0 @@ }); |
{ | ||
"name": "yr.no-forecast", | ||
"version": "0.0.3", | ||
"version": "0.0.4", | ||
"description": "Module to easily retrieve simple weather forecast for a given time and location.", | ||
@@ -15,2 +15,3 @@ "main": "./index.js", | ||
}, | ||
"license": "MIT", | ||
"repository": { | ||
@@ -17,0 +18,0 @@ "type": "git", |
@@ -8,3 +8,3 @@ yr.no-forecast | ||
###Usage | ||
Use the ```getWeather(queryStringParams, callback|stream, [VERSION])``` function to get a LocationForecast object. This object has functions that take a callback as parameter and are detailed in the example below. | ||
Use the ```getWeather(queryStringParams, callback|stream, [VERSION])``` function to get a LocationForecast object, where version represents the version of the "locationforecast" service to use. This object has functions that take a callback as parameter and are detailed in the example below. | ||
@@ -26,21 +26,23 @@ ``` | ||
### Weather JSON Format | ||
Format is inspired by that of [forecast.io](https://developer.forecast.io/) service. Not all fields will always be available. Fields that no data was retrieved for contain the null value; | ||
Format is somewhat inspired by that of [forecast.io](https://developer.forecast.io/) service. Not all fields will always be available. Fields that no data was retrieved for contain the null value; | ||
``` | ||
{ | ||
icon: 'PARTLYCLOUD', | ||
to: '2013-11-08T14:00:00Z', | ||
from: '2013-11-08T13:00:00Z', | ||
{ | ||
icon: 'PARTLYCLOUD', | ||
to: '2013-11-15T18:00:00Z', | ||
from: '2013-11-15T12:00:00Z', | ||
rain: '0.0 mm', | ||
temperature: '10.0', | ||
windSpeed: '5.0m/s', | ||
windBearing: '188.3', | ||
beaufort: '3', | ||
cloudCover: '77.0', | ||
humidity: '80.6%', | ||
pressure: '1006.3 hPa' | ||
lowCloud: null, | ||
highCloud: null | ||
temperature: '9.7 celcius', | ||
windDirection: { deg: '220.2', name: 'SW' }, | ||
windSpeed: { mps: '2.7', beaufort: '2', name: 'Svak vind' }, | ||
humidity: '27.9 percent', | ||
pressure: '1021.0 hPa', | ||
cloudiness: '0.0%', | ||
fog: '0.0%', | ||
lowClouds: '0.0%', | ||
mediumClouds: '0.0%', | ||
highClouds: '0.0%', | ||
dewpointTemperature: '-8.3 celcius', | ||
... | ||
} | ||
``` |
var assert = require('assert'), | ||
moment = require('moment'), | ||
lib = require('../index.js'); | ||
@@ -34,3 +35,3 @@ | ||
it('Should return an object with LocationForecast methods', function(done) { | ||
it('getWeather: Should return an object with LocationForecast methods', function(done) { | ||
lib.getWeather(LOCATIONS.DUBLIN, function(err, weather) { | ||
@@ -46,3 +47,3 @@ assert(!err); | ||
it('Should return array with 5 weather objects', function(done) { | ||
it('getFiveDaySummary: Should return array with 5 weather objects', function(done) { | ||
lib.getWeather(LOCATIONS.DUBLIN, function(err, weather) { | ||
@@ -61,3 +62,3 @@ assert(!err); | ||
it('Should return array with 5 weather objects', function(done) { | ||
it('getFiveDaySummary: Should return array with 5 weather objects', function(done) { | ||
lib.getWeather(LOCATIONS.NICE, function(err, weather) { | ||
@@ -74,3 +75,3 @@ weather.getFiveDaySummary(function(err, summary) { | ||
it('Should return array with 5 weather objects', function(done) { | ||
it('getFiveDaySummary: Should return array with 5 weather objects', function(done) { | ||
lib.getWeather(LOCATIONS.LA, function(err, weather) { | ||
@@ -87,3 +88,3 @@ weather.getFiveDaySummary(function(err, summary) { | ||
it('Should return array with 5 weather objects', function(done) { | ||
it('getFiveDaySummary: Should return array with 5 weather objects', function(done) { | ||
lib.getWeather(LOCATIONS.RYGGE, function(err, weather) { | ||
@@ -100,3 +101,3 @@ weather.getFiveDaySummary(function(err, summary) { | ||
it('Should return an object with info fields', function(done) { | ||
it('getCurrentSummary: Should return an object with info fields', function(done) { | ||
lib.getWeather(LOCATIONS.DUBLIN, function(err, weather) { | ||
@@ -113,3 +114,3 @@ weather.getCurrentSummary(function(err, summary) { | ||
it('Should return an object with info fields', function(done) { | ||
it('getCurrentSummary: Should return an object with info fields', function(done) { | ||
lib.getWeather(LOCATIONS.LA, function(err, weather) { | ||
@@ -126,3 +127,3 @@ weather.getCurrentSummary(function(err, summary) { | ||
it('Should return an object with info fields', function(done) { | ||
it('getCurrentSummary: Should return an object with info fields', function(done) { | ||
lib.getWeather(LOCATIONS.NICE, function(err, weather) { | ||
@@ -139,3 +140,3 @@ weather.getCurrentSummary(function(err, summary) { | ||
it('Should return an object with info fields', function(done) { | ||
it('getCurrentSummary: Should return an object with info fields', function(done) { | ||
lib.getWeather(LOCATIONS.RYGGE, function(err, weather) { | ||
@@ -150,2 +151,36 @@ weather.getCurrentSummary(function(err, summary) { | ||
}); | ||
}); | ||
}); | ||
describe('Test retrieval for various times', function() { | ||
this.timeout(10000); | ||
it('Should get weather for earliest time possible today by requesting some time yesterday', function(done) { | ||
var time = moment(); | ||
time.set('hours', (time.hours() - time.hours() - 4) ); | ||
lib.getWeather(LOCATIONS.DUBLIN, function(err, weather) { | ||
assert(!err); | ||
assert(weather); | ||
weather.getForecastForTime(time, function(err, forecast) { | ||
assert(!err); | ||
assert(forecast); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it('Should get weather for 9:20 PM (21:20) tonight', function(done) { | ||
var time = moment(); | ||
time.set('hours', 21); | ||
time.set('minutes', 20); | ||
lib.getWeather(LOCATIONS.DUBLIN, function(err, weather) { | ||
assert(!err); | ||
assert(weather); | ||
weather.getForecastForTime(time, function(err, forecast) { | ||
assert(!err); | ||
assert(forecast); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}) |
No License Found
License(Experimental) License information could not be found
Found 1 instance in 1 package
17101
6
0
461
47