weacast/weacast-arpege

View on GitHub
src/services/arpege/arpege.service.js

Summary

Maintainability
A
0 mins
Test Coverage
D
65%
import path from 'path'
import fs from 'fs-extra'
import gtiff2json from 'weacast-gtiff2json'
import logger from 'winston'
import makeDebug from 'debug'
const debug = makeDebug('weacast:weacast-arpege')

export default {

  // Perform conversion from input TIFF to JSON
  convertForecastTime (runTime, forecastTime) {
    // Limit precision because default accuracy is usually not required
    const replacer = (key, value) => value.toFixed ? Number(value.toFixed(this.element.precision || 2)) : value

    let promise = new Promise((resolve, reject) => {
      const filePath = this.getForecastTimeFilePath(runTime, forecastTime)
      const convertedFilePath = this.getForecastTimeConvertedFilePath(runTime, forecastTime)
      if (fs.existsSync(convertedFilePath)) {
        logger.verbose('Already converted ' + this.forecast.name + '/' + this.element.name + ' forecast at ' + forecastTime.format() + ' for run ' + runTime.format())
        fs.readJson(convertedFilePath, 'utf8')
        .then(grid => {
          resolve(grid)
        })
        .catch(error => {
          logger.error('Cannot read converted ' + this.forecast.name + '/' + this.element.name + ' forecast at ' + forecastTime.format() + ' for run ' + runTime.format())
          debug('Input JSON file was : ' + convertedFilePath)
          reject(error)
        })
        return
      }

      gtiff2json(filePath)
      .then(grid => {
        logger.verbose('Converted ' + this.forecast.name + '/' + this.element.name + ' forecast at ' + forecastTime.format() + ' for run ' + runTime.format())
        // Change extension from tiff to json
        fs.outputJson(convertedFilePath, grid, { encoding: 'utf8', replacer })
        .then(_ => {
          logger.verbose('Written ' + this.forecast.name + '/' + this.element.name + ' converted forecast at ' + forecastTime.format() + ' for run ' + runTime.format())
          resolve(grid)
        })
        .catch(error => {
          logger.error('Cannot write converted ' + this.forecast.name + '/' + this.element.name + ' forecast at ' + forecastTime.format() + ' for run ' + runTime.format())
          debug('Output JSON file was : ' + convertedFilePath)
          reject(error)
        })
      })
      .catch(error => {
        reject(error)
      })
    })

    return promise
  },

  // Generate file name to store temporary input data with the right format extension
  getForecastTimeFilePath (runTime, forecastTime) {
    return path.join(this.app.get('forecastPath'), this.forecast.name, this.element.name, runTime.format('YYYY-MM-DD[_]HH-mm-ss') + '_' + forecastTime.format('YYYY-MM-DD[_]HH-mm-ss') + '.tiff')
  },

  // Build the request options to download given forecast time from input WCS data source
  getForecastTimeRequest (runTime, forecastTime) {
    let suffix = ''
    // Check for accumulation period on accumulated elements
    let accumulationPeriod = this.element.accumulationPeriod || 0
    if (accumulationPeriod > 0) {
      // Jump to hours
      accumulationPeriod = accumulationPeriod / 3600
      // When less than 1 day suffix is PTXH
      if (accumulationPeriod < 24) suffix = '_PT' + accumulationPeriod + 'H'
      // When more than 1 day suffix is PXD
      else suffix = '_P' + (accumulationPeriod / 24) + 'D'
    }
    // Setup request with URL, token, subset parameters for WCS
    let queryParameters = {
      apikey: this.forecast.token,
      REQUEST: 'GetCoverage',
      coverageid: this.element.coverageid + '___' + runTime.format() + suffix,
      subset: []
    }
    if (this.element.subsets) {
      Object.entries(this.element.subsets).forEach(subset => {
        queryParameters.subset.push(subset[0] + '(' + subset[1] + ')')
      })
    }
    queryParameters.subset.push('time(' + forecastTime.format() + ')')
    return {
      url: this.forecast.wcsBaseUrl,
      qs: queryParameters,
      qsStringifyOptions: { arrayFormat: 'repeat' }
    }
  }
}