Models/createCatalogItemFromFileOrUrl.js

"use strict";

/*global require*/
var defaultValue = require("terriajs-cesium/Source/Core/defaultValue").default;
var when = require("terriajs-cesium/Source/ThirdParty/when").default;

var createCatalogItemFromUrl = require("./createCatalogItemFromUrl");
var createCatalogMemberFromType = require("./createCatalogMemberFromType");
var TerriaError = require("../Core/TerriaError");
var defined = require("terriajs-cesium/Source/Core/defined").default;
var OgrCatalogItem = require("../Models/OgrCatalogItem");
var i18next = require("i18next").default;

/**
 * Asynchronously creates and loads a catalog item for a given file.  The returned promise does not resolve until
 * the catalog item is successfully loaded, and it rejects if the file is not in the expected format or
 * another error occurs during loading.  If the OGR-based conversion service needs to be invoked to convert the file or URL
 * to a compatible format, the user
 *
 * @param  {Terria} terria The Terria instance in which to create the item.
 * @param {File|String} fileOrUrl The file or URL for which to create a catalog item.
 * @param {String} [dataType='auto'] The type of catalog item to create.  If 'auto', the type is deduced from the URL or filename.
 *                                   If 'other', the OGR-based conversion service is used.  This can also be any valid catalog item
 *                                   {@link CatalogItem#type}.
 * @param {Boolean} [confirmConversion=true] If true, and the OGR-based conversion service needs to be invoked, the user will first
 *                                           be asked for permission to upload the file to the conversion service.  If false, the
 *                                           user will not be asked for permission.  If the user denies the request, the promise
 *                                           will be rejected with the string 'The user declined to use the conversion service.'.
 * @return {Promise} A promise that resolves to the created catalog item.
 */
var createCatalogItemFromFileOrUrl = function(
  terria,
  viewState,
  fileOrUrl,
  dataType,
  confirmConversion
) {
  function tryConversionService(newItem) {
    if (terria.configParameters.conversionServiceBaseUrl === false) {
      // Don't allow conversion service. Duplicated in OgrCatalogItem.js
      terria.error.raiseEvent(
        new TerriaError({
          title: i18next.t("models.catalog.unsupportedFileTypeTitle"),
          message: i18next.t("models.catalog.unsupportedFileTypeMessage", {
            appName: terria.appName,
            link:
              '<a href="https://github.com/TerriaJS/nationalmap/wiki/csv-geo-au">csv-geo-au format</a>'
          })
        })
      );
      return undefined;
    } else if (
      name.match(
        /\.(shp|jpg|jpeg|pdf|xlsx|xls|tif|tiff|png|txt|doc|docx|xml|json)$/
      )
    ) {
      terria.error.raiseEvent(
        new TerriaError({
          title: i18next.t("models.catalog.unsupportedFileTypeTitle"),
          message: i18next.t("models.catalog.unsupportedFileTypeMessageII", {
            appName: terria.appName,
            link:
              '<a href="https://github.com/TerriaJS/nationalmap/wiki/csv-geo-au">csv-geo-au format</a>',
            linkII:
              '<a href="http://www.gdal.org/ogr_formats.html">OGR Vector Formats</a>'
          })
        })
      );
      return undefined;
    }
    return getConfirmation(
      terria,
      viewState,
      confirmConversion,
      i18next.t("models.catalog.getConfirmationMessage", {
        appName: terria.appName
      })
    ).then(function(confirmed) {
      return confirmed
        ? loadItem(new OgrCatalogItem(terria), name, fileOrUrl)
        : undefined;
    });
  }

  var isUrl = typeof fileOrUrl === "string";
  dataType = defaultValue(dataType, "auto");

  var name = isUrl ? fileOrUrl : fileOrUrl.name;

  if (dataType === "auto") {
    return when(createCatalogItemFromUrl(name, terria, isUrl)).then(function(
      newItem
    ) {
      //##Doesn't work for file uploads
      if (!defined(newItem)) {
        return tryConversionService();
      } else {
        // It's a file or service we support directly
        // In some cases (web services), the item will already have been loaded by this point.
        return loadItem(newItem, name, fileOrUrl);
      }
    });
  } else if (dataType === "other") {
    // user explicitly chose "Other (use conversion service)"
    return getConfirmation(
      terria,
      viewState,
      confirmConversion,
      i18next.t("models.catalog.readyToUpload", {
        appName: terria.appName
      })
    ).then(function(confirmed) {
      return confirmed
        ? loadItem(createCatalogMemberFromType("ogr", terria), name, fileOrUrl)
        : undefined;
    });
  } else {
    // User has provided a type, so we go with that.
    return loadItem(
      createCatalogMemberFromType(dataType, terria),
      name,
      fileOrUrl
    );
  }
};

/* Returns a promise that returns true if user confirms, or false if they abort. */
function getConfirmation(terria, viewState, confirmConversion, message) {
  if (!confirmConversion) {
    return when(true);
  }

  var d = when.defer(); // there's no `when.promise(resolver)` in when 1.7.1
  viewState.notifications.push({
    confirmText: i18next.t("models.catalog.upload"),
    denyText: i18next.t("models.catalog.cancel"),
    title: i18next.t("models.catalog.useConversion"),
    message: message,
    confirmAction: function() {
      d.resolve(true);
    },
    denyAction: function() {
      d.resolve(false);
    }
  });
  return d.promise;
}

function loadItem(newCatalogItem, name, fileOrUrl) {
  if (typeof fileOrUrl === "string") {
    newCatalogItem.url = fileOrUrl;
  } else {
    newCatalogItem.data = fileOrUrl;
    newCatalogItem.dataSourceUrl = fileOrUrl.name;
    newCatalogItem.dataUrlType = "local";
    newCatalogItem.name = fileOrUrl.name;
  }

  return when(newCatalogItem.load()).yield(newCatalogItem);
}

module.exports = createCatalogItemFromFileOrUrl;