Source: helpers/converters.js

/**
 * @see module:helpers
 * @module helpers/converters
 */

/**
 * Converts the given input to degrees, using the provided min and max values.
 *
 * @function
 * @param {Number} input - Variable to convert to degrees.
 * @param {Number} min - Minimum value of the range.
 * @param {Number} max - Maximu value of the range.
 * @returns {Number} - Returns a between or equal to 0 - 360.
 */
function calculateDegrees(input, min, max) {
  if (typeof input !== 'number') {
    throw new Error('Expected \'input\' to be a number!');
  }

  if (typeof min !== 'number') {
    throw new Error('Expected \'min\' to be a number!');
  }

  if (typeof max !== 'number') {
    throw new Error('Expected \'max\' to be a number!');
  }

  if (input < Math.min(min, max)) {
    throw new Error('Expected \'input\' to be equal to or between \'min\' and or \'max\'!');
  }

  if (input > Math.max(min, max)) {
    throw new Error('Expected \'input\' to be equal to or between \'min\' and or \'max\'!');
  }

  const range = Math.max(min, max) - Math.min(min, max);
  return Math.round(((input - Math.min(min, max)) / range) * 360);
}

/**
 * Converts degrees to a color (RGB).
 *
 * @function
 * @param {Number} degrees - Degrees to convert to a color.
 * @returns {Number[]} - Red, Green and Blue
 */
function degreesToColor(degrees) {
  if (typeof degrees !== 'number') {
    throw new Error('Expected \'degrees\' to be a number!');
  }

  if (degrees < 0 || degrees > 360) {
    throw new Error('Expected \'degrees\' to be within 0 - 360!');
  }

  let value = 0;

  // [255, 0, 0] -> [255, 255, 0]
  if (degrees <= 60) {
    value = Math.round((degrees / 60) * 255);
    return [255, value, 0];
  }

  // [255, 255, 0] -> [0, 255, 0]
  if (degrees <= 120) {
    value = 255 - Math.round(((degrees - 60) / 60) * 255);
    return [value, 255, 0];
  }

  // [0, 255, 0] -> [0, 255, 255]
  if (degrees <= 180) {
    value = Math.round(((degrees - 120) / 60) * 255);
    return [0, 255, value];
  }

  // [0, 255, 255] -> [0, 0, 255]
  if (degrees <= 240) {
    value = 255 - Math.round(((degrees - 180) / 60) * 255);
    return [0, value, 255];
  }

  // [0, 0, 255] -> [255, 0, 255]
  if (degrees <= 300) {
    value = Math.round(((degrees - 240) / 60) * 255);
    return [value, 0, 255];
  }

  // [255, 0, 255] -> [255, 0, 0]
  value = 255 - Math.round(((degrees - 300) / 60) * 255);
  return [255, 0, value];
}

/**
 * Converts the Z-level of an OpenLayers map to an increment.
 *
 * @function
 * @param {Number} z - Z-level of the OpenLayers map.
 * @returns {Number} - Increment.
 */
function getIncrement(z) {
  if (typeof z !== 'number') {
    throw new Error('Expected \'z\' to be a number!');
  }

  return Math.min(2 ** Math.max((15 - z), 3), 128);
}

/**
 * Converts a tile-coordinate to latitude and longitude.
 *
 * @function
 * @param {Number} z - The z-coordinate.
 * @param {Number} x - The x-coordinate.
 * @param {Number} y - The y-coordinate.
 * @returns {Number[]} - Latitude and Longitude
 */
function getLatLong({ z, x, y }) {
  if (typeof z !== 'number') {
    throw new Error('Expected \'z\' to be a number!');
  }

  if (typeof x !== 'number') {
    throw new Error('Expected \'x\' to be a number!');
  }

  if (typeof y !== 'number') {
    throw new Error('Expected \'y\' to be a number!');
  }

  const latitude = tileToLat(y, z);
  const longitude = tileToLong(x, z);

  return [latitude, longitude];
}

/**
 * Subtracts a given number of days from a Date.
 *
 * @function
 * @param {Date} date - Date object to subtract from.
 * @param {Number} numberOfDays - Number of days to subtract.
 * @returns {Date} - Red, Green and Blue
 */
function subtractDays(date, numberOfDays) {
  if (!(date instanceof Date)) {
    throw new Error('Expected \'date\' to be an instance of Date!');
  }

  if (typeof numberOfDays !== 'number') {
    throw new Error('Expected \'numberOfDays\' to be a number!');
  }

  const unixDays = numberOfDays * (24 * 60 * 60 * 1000);
  const unixDate = date.getTime();

  return new Date(unixDate - unixDays);
}

/**
 * Converts temperature, in °C, to a color (RGB).
 *
 * @function
 * @param {Number} temperature - Temperature to convert to a color.
 * @returns {Number[]} - Red, Green and Blue
 */
function temperatureToColor(temperature) {
  if (typeof temperature !== 'number') {
    throw new Error('Expected \'temperature\' to be a number!');
  }

  if (temperature < -50 || temperature > 50) {
    throw new Error('Expected \'temperature\' to be equal to or between -50°C and or 50°C!');
  }

  const degrees = temperatureToDegrees(temperature);
  return degreesToColor(degrees);
}

/**
 * Converts temperature, in °C, to degrees.
 *
 * @function
 * @param {Number} temperature - Temperature to convert to degrees.
 * @returns {Number} 0 - 300
 */
function temperatureToDegrees(temperature) {
  if (typeof temperature !== 'number') {
    throw new Error('Expected \'temperature\' to be a number!');
  }

  if (temperature < -50 || temperature > 50) {
    throw new Error('Expected \'temperature\' to be equal to or between -50°C and or 50°C!');
  }

  const degrees = calculateDegrees(temperature, -50, 50);
  return Math.round(((360 - degrees) / 360) * 300);
}

/**
 * Converts a tile with y- and z-coordinates to a latitude.
 *
 * @param {Number} y - The y-coordinate.
 * @param {Number} z - The z-coordinate.
 * @returns {Number} - The latitude.
 */
function tileToLat(y, z) {
  if (typeof y !== 'number') {
    throw new Error('Expected \'y\' to be a number!');
  }

  if (typeof z !== 'number') {
    throw new Error('Expected \'z\' to be a number!');
  }

  const n = Math.PI - (((2 * Math.PI) * (y + 0.525)) / (2 ** z));
  return ((180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))));
}

/**
 * Converts a tile with x- and z-coordinates to a longitude.
 *
 * @param {Number} x - The x-coordinate.
 * @param {Number} z - The z-coordinate.
 * @returns {Number} - The longitude.
 */
function tileToLong(x, z) {
  if (typeof x !== 'number') {
    throw new Error('Expected \'x\' to be a number!');
  }

  if (typeof z !== 'number') {
    throw new Error('Expected \'z\' to be a number!');
  }

  return ((((x + 0.525) / (2 ** z)) * 360) - 180);
}

module.exports = {
  calculateDegrees,
  degreesToColor,
  getIncrement,
  getLatLong,
  subtractDays,
  temperatureToColor,
  temperatureToDegrees,
  tileToLat,
  tileToLong,
};