API Docs for: 0.0.1
Show:

File: lib/values/hardness.js

/*
* niViz -- snow profiles visualization
* Copyright (C) 2015 WSL/SLF - Fluelastrasse 11 - 7260 Davos Dorf - Switzerland.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

(function (niviz) {
  'use strict';

  // --- Module Dependencies ---
  var properties = Object.defineProperties;
  var property   = Object.defineProperty;

  var Value      = niviz.Value;
  var Common     = niviz.Common;
  var inherits   = niviz.util.inherits;

  /** @module niviz.Value */

  var EXPONENT    = 2.4;
  var FACTOR      = 1 / EXPONENT;
  var COEFFICIENT = 19.3;

  /**
   * Stores a Hand Hardness Index (HHI) value.
   *
   * @class Hardness
   * @extends Value
   *
   * @constructor
   * @param {Number} [top]
   * @param {Number} [value]
   * @param {Number} [bottom]
   */
  function Hardness(top, val, bottom) {

    if (typeof val === 'string' || typeof val === 'number' && val > 6)
      val = Hardness.parse(val);

    /**
     * The HHI value between .666 and 6.0.
     *
     * @property value
     * @type {Number}
     */
    var value;

    property(this, 'value', {
      enumerable: true,

      get: function () { return value; },

      set: function value$set(to) {
        value = this.$newton = undefined;

        if (to) {
          var v = parseFloat(to);

          if (isNaN(v) || v < .666 || v > 6)
            throw new Error('HHI index must be between .666 (F-) and 6.0, was: ' + to);

          value = v;
        }
      }
    });

    // Prepare Newton cache
    property(this, '$newton', { writable: true, enumerable: false });

    // Call Value constructor
    Value.call(this, top, val, bottom);
  }

  inherits(Hardness, Value);

  properties(Hardness, {
    /**
     * HHI code to numeric index mapping.
     *
     * @property index
     * @type {Object}
     */
    index: {
      value: {
        'F-':  2 / 3,
        'F':  1,
        'F+':  4 / 3,
        'F-4F':  1.5,
        '4F-': 5 / 3,
        '4F': 2,
        '4F+': 7 / 3,
        '4F-1F': 2.5,
        '1F-': 8 / 3,
        '1F': 3,
        '1F+': 10 / 3,
        '1F-P': 3.5,
        'P-':  11 / 3,
        'P':  4,
        'P+':  13 / 3,
        'P-K':  4.5,
        'K-':  14 / 3,
        'K':  5,
        'K+':  16 / 3,
        'K-I':  5.5,
        'I':  6
      }
    },

    /**
     * @property codes
     * @type {Array.<String>}
     */
    codes: {
      value: ['F', '4F', '1F', 'P', 'K', 'I']
    },

    /**
     * @property options
     * @type {Array.<String>}
     */
    caamlcodes: {
      value: [ 'F-', 'F', 'F+', 'F-4F', '4F-', '4F', '4F+', '4F-1F', '1F-', '1F', '1F+',
               '1F-P', 'P-', 'P', 'P+', 'P-K', 'K-', 'K', 'K+', 'K-I', 'I' ]
    },

    allcodes: {
      get: function allcodes$get() {
        if (Common.defaults.hand_hardness_format === 'International')
          return [  'F', 'F-4F', '4F', '4F-1F', '1F', '1F-P', 'P', 'P-K', 'K', 'K-I', 'I',
                    'F-', 'F+', '4F-', '4F+', '1F-', '1F+', 'P-', 'P+', 'K-', 'K+' ]
        else
          return [ 'F-', 'F', 'F+', '4F-', '4F', '4F+', '1F-', '1F', '1F+', 'P-', 'P', 'P+',
                   'K-', 'K', 'K+', 'I', 'F-4F', '4F-1F', '1F-P', 'P-K', 'K-I']
      }
    }
  });

  /**
   * Converts a `value` to HHI index. If the value
   * is a string, it is interpreted as an HHI code;
   * if it is a number it is interpreted as ram
   * resistance in Newton.
   *
   * @method parse
   * @static
   *
   * @param {Number, String} value
   *
   * @return {Number}
   */
  Hardness.parse = function (value) {
    if (!value) return null;

    switch (typeof value) {
    case 'number':
      if (value >= 1200) return 6;
      if (value <= COEFFICIENT) return 1;

      return Math.pow(value / COEFFICIENT, FACTOR);

    case 'string':
      var idx = Hardness.index[value];

      if (!idx)
        throw new Error('Unknown HHI code: ' + value);

      return idx;
      //return [value].reduce(avg, 0);
      //return value.split(/\s*-\s*/).reduce(avg, 0);

    default:
      throw new Error('Cannot convert value to HHI index: ' + value);
    }
  };

  properties(Hardness.prototype, {
    /**
     * HHI code string.
     *
     * @property code
     * @type {String}
     */
    code: {
      get: function code$get() {
        if (!this.value) return undefined;

        var lower = Math.floor(this.value);

        if (this.value < 1) return Hardness.codes[0];

        if (this.value === lower)
          return Hardness.codes[lower - 1];

        return [
          Hardness.codes[lower - 1],
          Hardness.codes[Math.ceil(this.value) - 1]
        ].join('-');
      },

      set: function code$set(value) {
        this.value = (value != null) ?
          Hardness.parse(String(value)) : undefined;
      }
    },

    /**
     * HHI CAAML code string.
     *
     * @property caamlcode
     * @type {String}
     */
    caamlcode: {
      get: function caamlcode$get() {
        if (!this.value) return undefined;

        var lower = Math.floor(this.value);

        if (this.value < 1) return Hardness.caamlcodes[0];
        if (this.value > 5.6) return 'I';

        if (this.value === lower) // exact value
          return Hardness.caamlcodes[(lower - 1) * 4 + 1];
        else if (this.value - lower > 0.3 && this.value - lower < 0.4)
          return Hardness.caamlcodes[(lower - 1) * 4 + 2];
        else if (this.value - lower > 0.6 && this.value - lower < 0.7)
          return Hardness.caamlcodes[lower * 4];

        return [
          Hardness.codes[lower - 1],
          Hardness.codes[Math.ceil(this.value) - 1]
        ].join('-');
      },

      set: function caamlcode$set(value) {
        this.value = (value != null) ?
          Hardness.parse(String(value)) : undefined;
      }
    },

    /**
     * Ram Resistance in Newton.
     *
     * @property newton
     * @type {Number}
     */
    newton: {
      get: function newton$get() {
        if (!this.value) return undefined;

        if (this.$newton == null) {
          if ((this.value * 10) % 5 === 0 && this.value <= 6 && this.value >= 1) {
            if (this.value === 1) this.$newton = 20;
            else if (this.value === 1.5) this.$newton = 50;
            else if (this.value === 2) this.$newton = 100;
            else if (this.value === 2.5) this.$newton = 175;
            else if (this.value === 3) this.$newton = 250;
            else if (this.value === 3.5) this.$newton = 390;
            else if (this.value === 4) this.$newton = 500;
            else if (this.value === 4.5) this.$newton = 715;
            else if (this.value === 5) this.$newton = 1000;
            else if (this.value === 5.5) this.$newton = 1100;
            else if (this.value > 5.5) this.$newton = 1200;
          } else {
            this.$newton = Math.pow(this.value, EXPONENT) * COEFFICIENT;
          }
        }

        return this.$newton;
      },

      set: function newton$set(value) {
        this.value = (value != null) ?
          Hardness.parse(parseFloat(value)) : undefined;
      }
    }
  });

  // --- Helpers ---

  // Computes the average index value for a list of HHI codes.
  function avg(a, b, i, code) {
    var idx = Hardness.index[b];

    if (!idx)
      throw new Error('Unknown HHI code: ' + b);

    if (i < code.length - 1)
      return a + idx;

    return (a + idx) / code.length;
  }

  // --- Module Exports ---
  Value.Hardness = Hardness;

}(niviz));