Source: Models/GpxCatalogItem.js

'use strict';

/*global require*/
var toGeoJSON = require('@mapbox/togeojson');

var defined = require('terriajs-cesium/Source/Core/defined');
var defineProperties = require('terriajs-cesium/Source/Core/defineProperties');
var knockout = require('terriajs-cesium/Source/ThirdParty/knockout');
var when = require('terriajs-cesium/Source/ThirdParty/when');

var Metadata = require('./Metadata');
var TerriaError = require('../Core/TerriaError');
var CatalogItem = require('./CatalogItem');
var inherit = require('../Core/inherit');
var proxyCatalogItemUrl = require('./proxyCatalogItemUrl');

var GeoJsonCatalogItem = require('./GeoJsonCatalogItem');
var readText = require('../Core/readText');
var loadText = require('../Core/loadText');


/**
 * A {@link CatalogItem} representing GPX data.
 *
 * @alias GpxCatalogItem
 * @constructor
 * @extends GeoJsonCatalogItem
 *
 * @param {Terria} terria The Terria instance.
 * @param {String} [url] The URL from which to retrieve the GPX data.
 */
var GpxCatalogItem =  function(terria, url) {
    CatalogItem.call(this, terria);

    this._geoJsonItem = undefined;

    this.url = url;

    /**
     * Gets or sets the Gpx data, represented as a binary Blob, DOM Document, or a Promise for one of those things.
     * If this property is set, {@link CatalogItem#url} is ignored.
     * This property is observable.
     * @type {Blob|Document|Promise}
     */
    this.data = undefined;

    /**
     * Gets or sets the URL from which the {@link GpxCatalogItem#data} was obtained.  This may be used
     * to resolve any resources linked in the Gpx file, if any.
     * @type {String}
     */
    this.dataSourceUrl = undefined;

    knockout.track(this, ['data', 'dataSourceUrl']);

};

inherit(CatalogItem, GpxCatalogItem);

defineProperties(GpxCatalogItem.prototype, {
    /**
     * Gets the type of data member represented by this instance.
     * @memberOf GpxCatalogItem.prototype
     * @type {String}
     */
    type : {
        get : function() {
            return 'gpx';
        }
    },

    /**
     * Gets a human-readable name for this type of data source, 'GPX'.
     * @memberOf GpxCatalogItem.prototype
     * @type {String}
     */
    typeName : {
        get : function() {
            return 'GPX';
        }
    },

    /**
     * Gets the metadata associated with this data source and the server that provided it, if applicable.
     * @memberOf GpxCatalogItem.prototype
     * @type {Metadata}
     */
    metadata : {
        get : function() {
            var result = new Metadata();
            result.isLoading = false;
            result.dataSourceErrorMessage = 'This data source does not have any details available.';
            result.serviceErrorMessage = 'This service does not have any details available.';
            return result;
        }
    },
    /**
     * Gets the data source associated with this catalog item.
     * @memberOf GpxCatalogItem.prototype
     * @type {DataSource}
     */
    dataSource : {
        get : function() {
            return defined(this._geoJsonItem) ? this._geoJsonItem.dataSource : undefined;
        }
    }
});

GpxCatalogItem.prototype._getValuesThatInfluenceLoad = function() {
    return [this.url, this.data];
};

GpxCatalogItem.prototype._load = function() {
    this._geoJsonItem = new GeoJsonCatalogItem( this.terria);

    var that = this;

    if (defined(that.data)) {
        return when(that.data, function(data) {
            var promise;
            if (data instanceof Blob) {
                promise = readText(data);
            } else {
                promise = data;
            }

            return when(promise, function(text) {
                return loadGpxText(that, text);
            });
        });
    } else {
        return loadText(proxyCatalogItemUrl(that, that.url, '1d')).then(function(text) {
            return loadGpxText(that, text);
        }).otherwise(function() {
            errorLoading(that);
        });
    }
};

GpxCatalogItem.prototype._enable = function() {
    if (defined(this._geoJsonItem)) {
        this._geoJsonItem._enable();
    }
};

GpxCatalogItem.prototype._disable = function() {
    if (defined(this._geoJsonItem)) {
        this._geoJsonItem._disable();
    }
};

GpxCatalogItem.prototype._show = function() {
    if (defined(this._geoJsonItem)) {
        this._geoJsonItem._show();
    }
};

GpxCatalogItem.prototype._hide = function() {
    if (defined(this._geoJsonItem)) {
        this._geoJsonItem._hide();
    }
};

GpxCatalogItem.prototype.showOnSeparateMap = function(globeOrMap) {
    if (defined(this._geoJsonItem)) {
        return this._geoJsonItem.showOnSeparateMap(globeOrMap);
    }
};

function loadGpxText(gpxItem, text) {

    var dom = (new DOMParser()).parseFromString(text, 'text/xml');
    var geojson = toGeoJSON.gpx(dom);

    gpxItem._geoJsonItem.data = geojson;

    return gpxItem._geoJsonItem.load().then(function() {
        gpxItem.rectangle = gpxItem._geoJsonItem.rectangle;
        gpxItem.clock = gpxItem._geoJsonItem.clock;
    });
}

function errorLoading(gpxItem) {
    var terria = gpxItem.terria;
    throw new TerriaError({
        sender: gpxItem,
        title: 'Error loading GPX',
        message: '\
An error occurred while loading a GPX file.  This may indicate that the file is invalid or that it \
is not supported by '+terria.appName+'.  If you would like assistance or further information, please email us \
at <a href="mailto:'+terria.supportEmail+'">'+terria.supportEmail+'</a>.'
    });
}

module.exports = GpxCatalogItem;