get outlook data
This commit is contained in:
parent
37eb88a90d
commit
24855fd959
45
cors/outlook.js
Normal file
45
cors/outlook.js
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
// pass through api requests
|
||||||
|
|
||||||
|
// http(s) modules
|
||||||
|
const https = require('https');
|
||||||
|
|
||||||
|
// url parsing
|
||||||
|
const queryString = require('querystring');
|
||||||
|
|
||||||
|
// return an express router
|
||||||
|
module.exports = (req, res) => {
|
||||||
|
// add out-going headers
|
||||||
|
const headers = {};
|
||||||
|
headers['user-agent'] = '(WeatherStar 4000+, ws4000@netbymatt.com)';
|
||||||
|
headers['accept'] = req.headers.accept;
|
||||||
|
|
||||||
|
// get query paramaters if the exist
|
||||||
|
const queryParams = Object.keys(req.query).reduce((acc, key) => {
|
||||||
|
// skip the paramater 'u'
|
||||||
|
if (key === 'u') return acc;
|
||||||
|
// add the paramter to the resulting object
|
||||||
|
acc[key] = req.query[key];
|
||||||
|
return acc;
|
||||||
|
},{});
|
||||||
|
let query = queryString.encode(queryParams);
|
||||||
|
if (query.length > 0) query = '?' + query;
|
||||||
|
|
||||||
|
// get the page
|
||||||
|
https.get('https://www.cpc.ncep.noaa.gov/' + req.path + query, {
|
||||||
|
headers,
|
||||||
|
}, getRes => {
|
||||||
|
// pull some info
|
||||||
|
const {statusCode} = getRes;
|
||||||
|
// pass the status code through
|
||||||
|
res.status(statusCode);
|
||||||
|
|
||||||
|
// set headers
|
||||||
|
res.header('content-type', getRes.headers['content-type']);
|
||||||
|
res.header('last-modified', getRes.headers['last-modified']);
|
||||||
|
// pipe to response
|
||||||
|
getRes.pipe(res);
|
||||||
|
|
||||||
|
}).on('error', e=>{
|
||||||
|
console.error(e);
|
||||||
|
});
|
||||||
|
};
|
2
index.js
2
index.js
|
@ -10,10 +10,12 @@ app.set('view engine', 'ejs');
|
||||||
// cors pass through
|
// cors pass through
|
||||||
const corsPassThru = require('./cors');
|
const corsPassThru = require('./cors');
|
||||||
const radarPassThru = require('./cors/radar');
|
const radarPassThru = require('./cors/radar');
|
||||||
|
const outlookPassThru = require('./cors/outlook');
|
||||||
|
|
||||||
// cors pass-thru to api.weather.gov
|
// cors pass-thru to api.weather.gov
|
||||||
app.get('/stations/*', corsPassThru);
|
app.get('/stations/*', corsPassThru);
|
||||||
app.get('/Conus/*', radarPassThru);
|
app.get('/Conus/*', radarPassThru);
|
||||||
|
app.get('/products/*', outlookPassThru);
|
||||||
|
|
||||||
// version
|
// version
|
||||||
const version = require('./version');
|
const version = require('./version');
|
||||||
|
|
|
@ -26,11 +26,10 @@ class Almanac extends WeatherDisplay {
|
||||||
|
|
||||||
// get images for outlook
|
// get images for outlook
|
||||||
const imagePromises = [
|
const imagePromises = [
|
||||||
utils.image.load('https://www.cpc.ncep.noaa.gov/products/predictions/30day/off14_temp.gif'),
|
utils.image.load('products/predictions/30day/off14_temp.gif'),
|
||||||
utils.image.load('https://www.cpc.ncep.noaa.gov/products/predictions/30day/off14_prcp.gif'),
|
utils.image.load('products/predictions/30day/off14_prcp.gif'),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
// get sun/moon data
|
// get sun/moon data
|
||||||
const {sun, moon} = this.calcSunMoonData(weatherParameters);
|
const {sun, moon} = this.calcSunMoonData(weatherParameters);
|
||||||
|
|
||||||
|
@ -38,7 +37,7 @@ class Almanac extends WeatherDisplay {
|
||||||
const [outlookTemp, outlookPrecip] = await Promise.all(imagePromises);
|
const [outlookTemp, outlookPrecip] = await Promise.all(imagePromises);
|
||||||
|
|
||||||
console.log(outlookTemp,outlookPrecip);
|
console.log(outlookTemp,outlookPrecip);
|
||||||
const outlook = 1;
|
const outlook = this.parseOutlooks(weatherParameters.latitude, weatherParameters.longitude, outlookTemp, outlookPrecip);
|
||||||
|
|
||||||
// store the data
|
// store the data
|
||||||
this.data = {
|
this.data = {
|
||||||
|
@ -125,6 +124,114 @@ class Almanac extends WeatherDisplay {
|
||||||
return {phase: phaseName, date: moonDate};
|
return {phase: phaseName, date: moonDate};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// use the color of the pixel to determine the outlook
|
||||||
|
parseOutlooks(lat, lon, temp, precip) {
|
||||||
|
const {DateTime} = luxon;
|
||||||
|
const month = DateTime.local().toLocaleString({month: 'long'});
|
||||||
|
|
||||||
|
// draw the images on the canvases
|
||||||
|
const tempContext = utils.image.drawLocalCanvas(temp);
|
||||||
|
const precipContext = utils.image.drawLocalCanvas(precip);
|
||||||
|
|
||||||
|
// get the color from each canvas
|
||||||
|
const tempColor = this.getOutlookColor(lat, lon, tempContext);
|
||||||
|
const precipColor = this.getOutlookColor(lat, lon, precipContext);
|
||||||
|
|
||||||
|
return {
|
||||||
|
month,
|
||||||
|
temperature: this.getOutlookTemperatureIndicator(tempColor),
|
||||||
|
precipitation: this.getOutlookPrecipitationIndicator(precipColor),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getOutlookColor (lat, lon, context) {
|
||||||
|
let x = 0;
|
||||||
|
let y = 0;
|
||||||
|
|
||||||
|
// The height is in the range of latitude 75'N (top) - 15'N (bottom)
|
||||||
|
y = ((75 - lat) / 53) * 707;
|
||||||
|
|
||||||
|
if (lat < 48.83) {
|
||||||
|
y -= Math.abs(48.83 - lat) * 2.9;
|
||||||
|
}
|
||||||
|
if (lon < -100.46) {
|
||||||
|
y -= Math.abs(-100.46 - lon) * 1.7;
|
||||||
|
} else {
|
||||||
|
y -= Math.abs(-100.46 - lon) * 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The width is in the range of the longitude ???
|
||||||
|
x = ((-155 - lon) / -110) * 719; // -155 - -40
|
||||||
|
|
||||||
|
if (lon < -100.46) {
|
||||||
|
x -= Math.abs(-100.46 - lon) * 1;
|
||||||
|
|
||||||
|
if (lat > 40) {
|
||||||
|
x += Math.abs(40 - lat) * 4;
|
||||||
|
} else {
|
||||||
|
x -= Math.abs(40 - lat) * 4;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x += Math.abs(-100.46 - lon) * 2;
|
||||||
|
|
||||||
|
if (lat < 36 && lon > -90) {
|
||||||
|
x += Math.abs(36 - lat) * 8;
|
||||||
|
} else {
|
||||||
|
x -= Math.abs(36 - lat) * 6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The further left and right from lat 45 and lon -97 the y increases
|
||||||
|
x = Math.round(x);
|
||||||
|
y = Math.round(y);
|
||||||
|
|
||||||
|
// Determine if there is any "non-white" colors around the area.
|
||||||
|
// Search a 16x16 region.
|
||||||
|
for (let colorX = x - 8; colorX <= x + 8; colorX++) {
|
||||||
|
for (let colorY = y - 8; colorY <= y + 8; colorY++) {
|
||||||
|
const pixelColor = this.getPixelColor(context, colorX, colorY);
|
||||||
|
if ((pixelColor.r !== 0 && pixelColor.g !== 0 && pixelColor.b !== 0) ||
|
||||||
|
(pixelColor.r !== 255 && pixelColor.g !== 255 && pixelColor.b !== 255)) {
|
||||||
|
return pixelColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get rgb values of a pixel
|
||||||
|
getPixelColor (context, x, y) {
|
||||||
|
const pixelData = context.getImageData(x, y, 1, 1).data;
|
||||||
|
return {
|
||||||
|
r: pixelData[0],
|
||||||
|
g: pixelData[1],
|
||||||
|
b: pixelData[2],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// get temperature outlook from color
|
||||||
|
getOutlookTemperatureIndicator(pixelColor) {
|
||||||
|
if (pixelColor.b > pixelColor.r) {
|
||||||
|
return 'B';
|
||||||
|
} else if (pixelColor.r > pixelColor.b) {
|
||||||
|
return 'A';
|
||||||
|
} else {
|
||||||
|
return 'N';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get precipitation outlook from color
|
||||||
|
getOutlookPrecipitationIndicator (pixelColor) {
|
||||||
|
if (pixelColor.g > pixelColor.r) {
|
||||||
|
return 'A';
|
||||||
|
} else if (pixelColor.r > pixelColor.g) {
|
||||||
|
return 'B';
|
||||||
|
} else {
|
||||||
|
return 'N';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async drawCanvas() {
|
async drawCanvas() {
|
||||||
super.drawCanvas();
|
super.drawCanvas();
|
||||||
const info = this.data;
|
const info = this.data;
|
||||||
|
|
|
@ -58,6 +58,22 @@ const utils = (() => {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// draw an image on a local canvas and return the context
|
||||||
|
const drawLocalCanvas = (img) => {
|
||||||
|
// create a canvas
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = img.width;
|
||||||
|
canvas.height = img.height;
|
||||||
|
|
||||||
|
// get the context
|
||||||
|
const context = canvas.getContext('2d');
|
||||||
|
context.imageSmoothingEnabled = false;
|
||||||
|
|
||||||
|
// draw the image
|
||||||
|
context.drawImage(img, 0,0);
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
// *********************************** unit conversions ***********************
|
// *********************************** unit conversions ***********************
|
||||||
|
|
||||||
Math.round2 = (value, decimals) => Number(Math.round(value + 'e' + decimals) + 'e-' + decimals);
|
Math.round2 = (value, decimals) => Number(Math.round(value + 'e' + decimals) + 'e-' + decimals);
|
||||||
|
@ -374,6 +390,7 @@ const utils = (() => {
|
||||||
load: loadImg,
|
load: loadImg,
|
||||||
superGifAsync,
|
superGifAsync,
|
||||||
preload,
|
preload,
|
||||||
|
drawLocalCanvas,
|
||||||
},
|
},
|
||||||
weather: {
|
weather: {
|
||||||
getPoint,
|
getPoint,
|
||||||
|
|
Loading…
Reference in a new issue