/**
* API controller
*
* @module controllers/api
* @see module:controllers
*/
/* Config */
const config = require('./../config/all');
/* Packages */
const express = require('express');
const fs = require('fs');
const Jimp = require('jimp');
const path = require('path');
/* Models */
const Data = require('./../models/data');
const SensorHub = require('./../models/sensorhub');
const Config = require('./../models/config');
/* Constants */
const router = express.Router();
/* Middlewares */
const {
isLoggedIn,
} = require('./../middlewares');
/* Helpers */
const {
downloadImage,
} = require('./../helpers/image');
const {
generateImage,
} = require('./../lib/generator');
const cacheFolder = path.resolve(`${__dirname}./../cache/`);
router.use((req, res, next) => {
if (!fs.existsSync(cacheFolder)) {
fs.mkdirSync(cacheFolder);
}
next();
});
const errorImage = path.resolve(cacheFolder, '-1_-1_-1.png');
router.use((req, res, next) => {
if (!fs.existsSync(errorImage)) {
const image = new Jimp(256, 256, 0x0);
image.write(errorImage);
}
next();
});
/**
* Handles the Mapbox tile services;
* also used for caching the tiles.
*
* @name Mapbox
* @path {GET} /api/mapbox/:z/:x/:y
* @params {String} :z is the z-coordinate.
* @params {String} :x is the x-coordinate.
* @params {String} :y is the y-coordinate.
*/
router.get('/mapbox/:z/:x/:y', isLoggedIn, (req, res, next) => {
const z = parseInt(req.params.z, 10);
const x = parseInt(req.params.x, 10);
const y = parseInt(req.params.y, 10);
const hostFolder = path.resolve(cacheFolder, 'mapbox');
if (!fs.existsSync(hostFolder)) {
fs.mkdirSync(hostFolder);
}
const filePath = path.resolve(hostFolder, `${z}_${x}_${y}.png`);
if (fs.existsSync(filePath)) {
res.sendFile(filePath);
return;
}
downloadImage(`https://a.tiles.mapbox.com/v3/planet.jh0b3oee/${z}/${x}/${y}.png`, {
host: 'mapbox',
name: `${z}_${x}_${y}.png`,
})
.then((img) => {
res.sendFile(img);
})
.catch((err) => {
next(err);
});
});
/**
* Handles the Planet tile services;
* also used for caching the tiles.
*
* @name Planet
* @path {GET} /api/planet/:datetime/:z/:x/:y
* @params {String} :dateTime unix-timestamp.
* @params {String} :z is the z-coordinate.
* @params {String} :x is the x-coordinate.
* @params {String} :y is the y-coordinate.
*/
router.get('/planet/:dateTime/:z/:x/:y', isLoggedIn, (req, res, next) => {
const z = parseInt(req.params.z, 10);
const x = parseInt(req.params.x, 10);
const y = parseInt(req.params.y, 10);
const hostFolder = path.resolve(cacheFolder, 'planet');
if (!fs.existsSync(hostFolder)) {
fs.mkdirSync(hostFolder);
}
const unixTimestamp = parseInt(req.params.dateTime, 10);
let currentdate = new Date(Math.floor(unixTimestamp / 1000 / 60 / 60 / 24) * 1000 * 60 * 60 * 24);
currentdate = currentdate.getTime() - (currentdate.getDate() * 24 * 60 * 60 * 1000);
const date = new Date(currentdate);
const unix = date.getTime();
const filePath = path.resolve(hostFolder, `${unix}_${z}_${x}_${y}.png`);
if (fs.existsSync(filePath)) {
res.sendFile(filePath);
return;
}
const today = new Date();
const month = today.getMonth(); // Jan = 0, Dec = 11
const year = today.getFullYear();
if (date.getFullYear() > year || (date.getMonth() > month && date.getFullYear() === year)) {
res.status(500);
res.sendFile(errorImage);
return;
}
let requestedYear = date.getFullYear();
let requestedMonth = (date.getMonth() + 2) % 12;
requestedMonth = requestedMonth === 0 ? 12 : requestedMonth;
if (requestedMonth >= month && requestedYear === year) {
requestedMonth = month - 1;
}
if (requestedMonth === 1) {
requestedYear += 1;
}
const planetYear = requestedYear;
const planetMonth = `0${requestedMonth}`.slice(-2);
const url = `https://tiles.planet.com/basemaps/v1/planet-tiles/global_monthly_${planetYear}_${planetMonth}_mosaic/gmap/${z}/${x}/${y}.png?api_key=${config.Planet.Key}`;
downloadImage(url, {
host: 'planet',
name: `${unix}_${z}_${x}_${y}.png`,
})
.then((img) => {
res.sendFile(img);
})
.catch((err) => {
next(err);
});
});
/**
* Handles the pop-menu data.
*
* @name Data
* @path {GET} /api/data/:sensorHub/:dateTime
* @params {String} :sensorHub SensorHub to get data from.
* @params {String} :dateTime unix-timestamp.
*/
router.get('/data/:sensorHub/:dateTime', isLoggedIn, (req, res, next) => {
const serialID = req.params.sensorHub;
const unixTimestamp = parseInt(req.params.dateTime, 10);
const requestedDate = new Date((Math.round(unixTimestamp / 1000 / 60 / 60)) * 1000 * 60 * 60);
SensorHub.findOne({
SerialID: serialID,
}).exec().then((sensorHub) => {
if (sensorHub == null) {
next(new Error());
return;
}
// eslint-disable-next-line consistent-return
return Data.find({
SensorHub: sensorHub.SerialID,
Timestamp: {
$gt: new Date(requestedDate.getTime() - (24 * 60 * 60 * 1000)),
$lte: new Date(requestedDate.getTime()),
},
}).sort({
Timestamp: -1,
}).exec().then(data => data);
})
.then((data) => {
const result = [
[
[],
[],
],
[
[],
[],
],
[
[],
[],
],
];
data.forEach((dataNode) => {
const type = dataNode.Type;
let index;
if (type === 'temperature') {
index = 0;
} else if (type === 'gasses') {
index = 1;
} else {
index = 2;
}
result[index][0].push(new Date(dataNode.Timestamp).toISOString());
result[index][1].push(parseInt(dataNode.Value, 10));
});
res.type('json');
res.send(JSON.stringify(result));
})
.catch((err) => {
next(err);
});
});
/**
* Handles the PDS tile services;
* also used for caching the tiles.
*
* @name Type
* @path {GET} /:type/:dateTime/:z/:x/:y
* @params {String} :type type of data.
* @params {String} :dateTime unix-timestamp.
* @params {String} :z is the z-coordinate.
* @params {String} :x is the x-coordinate.
* @params {String} :y is the y-coordinate.
*/
router.get('/:type/:dateTime/:z/:x/:y', isLoggedIn, (req, res, next) => {
/*
* const z = parseInt(req.params.z, 10);
* const x = parseInt(req.params.x, 10);
* const y = parseInt(req.params.y, 10);
*/
const {
type,
} = req.params;
const types = ['gasses', 'light', 'temperature'];
if (!types.includes(type)) {
res.status(501);
res.sendFile(errorImage);
return;
}
const hostFolder = path.resolve(cacheFolder, type);
if (!fs.existsSync(hostFolder)) {
fs.mkdirSync(hostFolder);
}
const unixTimestamp = parseInt(req.params.dateTime, 10);
const requestedDate = new Date((Math.round(unixTimestamp / 1000 / 60 / 60)) * 1000 * 60 * 60);
/*
* const filePath = path.resolve(hostFolder, `${requestedDate.getTime()}_${z}_${x}_${y}.png`);
* if (fs.existsSync(filePath)) {
* res.type('png');
* res.sendFile(filePath);
* return;
* }
*/
SensorHub.find({}).exec()
.then(sensorHubs => Data.find({
Type: type,
Timestamp: {
$gt: new Date(requestedDate.getTime() - (30 * 60 * 1000)),
$lte: new Date(requestedDate.getTime() + (30 * 60 * 1000)),
},
}).exec().then(data => [sensorHubs, data]))
.then(([sensorHubs, data]) => Config.findOne({
valueID: `treshold-${type}`,
}).exec().then(treshold => [sensorHubs, data, treshold]))
.then(([sensorHubs, data, treshold]) => generateImage(req.params, sensorHubs, data, treshold))
.then((image) => {
/*
* image.write(filePath);
*/
image.getBuffer(Jimp.MIME_PNG, (err, buffer) => {
if (err) {
next(err);
} else {
res.type('png');
res.send(buffer);
}
});
})
.catch((err) => {
next(err);
});
});
/**
* Renders a KML file containing the locations of all SensorHubs.
*
* @name SensorHubs
* @path {GET} /api/sensorhubs
*/
router.get('/sensorhubs', isLoggedIn, (req, res, next) => {
SensorHub.find({}).exec()
.then((sensorHubs) => {
res.render('sensorhubs', {
layout: false,
SensorHubs: sensorHubs,
});
})
.catch((err) => {
next(err);
});
});
/**
* Sends the errorImage to the user for any unhandled request.
*
* @name 404
* @path {ALL} /api/*
*/
router.all('*', isLoggedIn, (req, res) => {
res.status(404);
res.sendFile(errorImage);
});
/* Exports */
module.exports = router;