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
|
||||
const corsPassThru = require('./cors');
|
||||
const radarPassThru = require('./cors/radar');
|
||||
const outlookPassThru = require('./cors/outlook');
|
||||
|
||||
// cors pass-thru to api.weather.gov
|
||||
app.get('/stations/*', corsPassThru);
|
||||
app.get('/Conus/*', radarPassThru);
|
||||
app.get('/products/*', outlookPassThru);
|
||||
|
||||
// version
|
||||
const version = require('./version');
|
||||
|
|
|
@ -26,11 +26,10 @@ class Almanac extends WeatherDisplay {
|
|||
|
||||
// get images for outlook
|
||||
const imagePromises = [
|
||||
utils.image.load('https://www.cpc.ncep.noaa.gov/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_temp.gif'),
|
||||
utils.image.load('products/predictions/30day/off14_prcp.gif'),
|
||||
];
|
||||
|
||||
|
||||
// get sun/moon data
|
||||
const {sun, moon} = this.calcSunMoonData(weatherParameters);
|
||||
|
||||
|
@ -38,7 +37,7 @@ class Almanac extends WeatherDisplay {
|
|||
const [outlookTemp, outlookPrecip] = await Promise.all(imagePromises);
|
||||
|
||||
console.log(outlookTemp,outlookPrecip);
|
||||
const outlook = 1;
|
||||
const outlook = this.parseOutlooks(weatherParameters.latitude, weatherParameters.longitude, outlookTemp, outlookPrecip);
|
||||
|
||||
// store the data
|
||||
this.data = {
|
||||
|
@ -125,6 +124,114 @@ class Almanac extends WeatherDisplay {
|
|||
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() {
|
||||
super.drawCanvas();
|
||||
const info = this.data;
|
||||
|
|
|
@ -58,6 +58,22 @@ const utils = (() => {
|
|||
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 ***********************
|
||||
|
||||
Math.round2 = (value, decimals) => Number(Math.round(value + 'e' + decimals) + 'e-' + decimals);
|
||||
|
@ -374,6 +390,7 @@ const utils = (() => {
|
|||
load: loadImg,
|
||||
superGifAsync,
|
||||
preload,
|
||||
drawLocalCanvas,
|
||||
},
|
||||
weather: {
|
||||
getPoint,
|
||||
|
|
Loading…
Reference in a new issue