Models/PlacesLikeMeCatalogFunction.js

"use strict";

/*global require*/
var CatalogFunction = require("./CatalogFunction");
var CsvCatalogItem = require("./CsvCatalogItem");
var defined = require("terriajs-cesium/Source/Core/defined").default;

var extendLoad = require("./extendLoad");
var inherit = require("../Core/inherit");
var invokeTerriaAnalyticsService = require("./invokeTerriaAnalyticsService");
var TerriaError = require("../Core/TerriaError");
var RegionDataParameter = require("./RegionDataParameter");
var RegionParameter = require("./RegionParameter");
var RegionTypeParameter = require("./RegionTypeParameter");
var RuntimeError = require("terriajs-cesium/Source/Core/RuntimeError").default;
var updateRectangleFromRegion = require("./updateRectangleFromRegion");
var i18next = require("i18next").default;
/**
 * A Terria Spatial Inference function to identify regions that are _most like_ a given region according to a
 * given set of characteristics.
 *
 * @alias PlacesLikeMeCatalogfunction
 * @constructor
 * @extends CatalogFunction
 *
 * @param {Terria} terria The Terria instance.
 */
var PlacesLikeMeCatalogfunction = function(terria) {
  CatalogFunction.call(this, terria);

  this.url = undefined;
  this.name = i18next.t("models.placesLikeMe.regionsLikeThisName");
  this.description = i18next.t(
    "models.placesLikeMe.regionsLikeThisDescription"
  );

  this._regionTypeParameter = new RegionTypeParameter({
    terria: this.terria,
    catalogFunction: this,
    id: "regionType",
    name: i18next.t("models.placesLikeMe.regionTypeName"),
    description: i18next.t("models.placesLikeMe.regionTypeDescription")
  });

  this._regionParameter = new RegionParameter({
    terria: this.terria,
    catalogFunction: this,
    id: "region",
    name: i18next.t("models.placesLikeMe.regionName"),
    description: i18next.t("models.placesLikeMe.regionDescription"),
    regionProvider: this._regionTypeParameter
  });

  this._dataParameter = new RegionDataParameter({
    terria: this.terria,
    catalogFunction: this,
    id: "data",
    name: i18next.t("models.placesLikeMe.characteristics"),
    description: i18next.t("models.placesLikeMe.characteristicsDescription"),
    regionProvider: this._regionTypeParameter
  });

  this._parameters = [
    this._regionTypeParameter,
    this._regionParameter,
    this._dataParameter
  ];
};

inherit(CatalogFunction, PlacesLikeMeCatalogfunction);

Object.defineProperties(PlacesLikeMeCatalogfunction.prototype, {
  /**
   * Gets the type of data member represented by this instance.
   * @memberOf PlacesLikeMeCatalogfunction.prototype
   * @type {String}
   */
  type: {
    get: function() {
      return "places-like-me-function";
    }
  },

  /**
   * Gets a human-readable name for this type of data source, 'Spatial Detailing'.
   * @memberOf PlacesLikeMeCatalogfunction.prototype
   * @type {String}
   */
  typeName: {
    get: function() {
      return i18next.t("models.placesLikeMe.name");
    }
  },

  /**
   * Gets the parameters used to {@link CatalogProcess#invoke} to this function.
   * @memberOf PlacesLikeMeCatalogfunction
   * @type {CatalogProcessParameters[]}
   */
  parameters: {
    get: function() {
      return this._parameters;
    }
  }
});

PlacesLikeMeCatalogfunction.prototype._load = function() {};

/**
 * Invokes the function.
 * @return {ResultPendingCatalogItem} The result of invoking this process.  Because the process typically proceeds asynchronously, the result is a temporary
 *         catalog item that resolves to the real one once the process finishes.
 */
PlacesLikeMeCatalogfunction.prototype.invoke = function() {
  var region = this._regionParameter.value;
  var data = this._dataParameter.getRegionDataValue();

  if (!defined(region)) {
    throw new TerriaError({
      title: i18next.t("models.placesLikeMe.regioNotSelectedName"),
      message: i18next.t("models.placesLikeMe.regioNotSelectedDescription")
    });
  }

  var regionProvider = this._regionTypeParameter.value;

  var request = {
    algorithm: "placeslikeme",
    boundaries_name: regionProvider.regionType,
    region_codes: data.regionCodes,
    columns: data.columnHeadings,
    table: data.table,
    parameters: {
      query: region.id
    }
  };

  var regionIndex = regionProvider.regions.indexOf(region);

  var regionName = region.id;
  if (regionIndex >= 0) {
    regionName = regionProvider.regionNames[regionIndex] || regionName;
  }

  var name = i18next.t("models.placesLikeMe.placesLike", {
    regionName: regionName
  });

  var that = this;
  return invokeTerriaAnalyticsService(
    this.terria,
    name,
    this.url,
    request
  ).then(function(invocationResult) {
    var result = invocationResult.result;

    var csv = regionProvider.aliases[0] + ",Likeness\n";

    var likeness = result.likeness;
    if (data.regionCodes.length !== likeness.length) {
      throw new RuntimeError(i18next.t("models.placesLikeMe.likenessesError"));
    }

    for (var i = 0; i < data.regionCodes.length; ++i) {
      csv += data.regionCodes[i] + "," + Math.pow(likeness[i], 3) + "\n"; // Note: to bring out contrast, we are taking the cube of the likeness. Remove.
    }

    var catalogItem = new CsvCatalogItem(that.terria);
    catalogItem.name = name;
    catalogItem.data = csv;
    catalogItem.dataUrl = invocationResult.url;
    catalogItem.tableStyle.colorBins = 40;
    catalogItem.tableStyle.colorMap = [
      { color: "rgba(255,255,255,1.00)", offset: 0 },
      { color: "rgba(191, 191, 255, 1.0)", offset: 0.5 },
      { color: "rgba(0, 0, 255, 1.0)", offset: 1 }
    ];
    // {"color": "rgba(255,255,255,1.00)", "offset": 0},
    // {"color": "rgba(0,0,255,1.0)", "offset": 1}
    var description =
      'This is the result of invoking "' +
      that.name +
      '" at ' +
      invocationResult.startDate +
      " with these parameters:\n\n";
    description +=
      " * " +
      regionProvider.regionType +
      " Region: " +
      regionName +
      " (" +
      region.id +
      ")\n";
    description +=
      " * Characteristics: " + data.columnHeadings.join(", ") + "\n";

    catalogItem.description = description;

    extendLoad(catalogItem, function() {
      return updateRectangleFromRegion(catalogItem, regionProvider, region);
    });

    catalogItem.isEnabled = true;
  });
};

module.exports = PlacesLikeMeCatalogfunction;