UNPKG

terriajs

Version:

Geospatial data visualization platform.

226 lines (200 loc) 9.2 kB
'use strict'; /*global require*/ var defined = require('terriajs-cesium/Source/Core/defined'); var defaultValue = require('terriajs-cesium/Source/Core/defaultValue'); var defineProperties = require('terriajs-cesium/Source/Core/defineProperties'); var DeveloperError = require('terriajs-cesium/Source/Core/DeveloperError'); var knockout = require('terriajs-cesium/Source/ThirdParty/knockout'); var CatalogGroup = require('./CatalogGroup'); var when = require('terriajs-cesium/Source/ThirdParty/when'); /** * The view model for the data catalog. * * @param {Terria} terria The Terria instance. * * @alias Catalog * @constructor */ var Catalog = function(terria) { if (!defined(terria)) { throw new DeveloperError('terria is required'); } this._terria = terria; this._shareKeyIndex = {}; this._group = new CatalogGroup(terria); this._group.name = 'Root Group'; this._group.preserveOrder = true; /** * Gets or sets a flag indicating whether the catalog is currently loading. * @type {Boolean} */ this.isLoading = false; /** * Gets or sets an array of non-geospatial, chartable catalog items. * @type {CsvCatalogItem[]} */ this.chartableItems = []; knockout.track(this, ['isLoading', 'chartableItems']); knockout.defineProperty(this, 'userAddedDataGroup', { get: this.upsertCatalogGroup.bind(this, CatalogGroup, 'User-Added Data', 'The group for data that was added by the user via the Add Data panel.') }); }; defineProperties(Catalog.prototype, { /** * Gets the Terria instance. * @memberOf Catalog.prototype * @type {Terria} */ terria : { get : function() { return this._terria; } }, /** * Gets the catalog's top-level group. * @memberOf Catalog.prototype * @type {CatalogGroup} */ group : { get : function() { return this._group; } }, /** * A flat index of all catalog member in this catalog by their share keys. Because items can have multiple share keys * to preserve backwards compatibility, multiple entries in this index will lead to the same catalog member. * * @type {Object} */ shareKeyIndex : { get : function() { return this._shareKeyIndex; } } }); /** * Updates the catalog from a JSON object-literal description of the available collections. * Existing collections with the same name as a collection in the JSON description are * updated. If the description contains a collection with a name that does not yet exist, * it is created. Because parts of the update may happen asynchronously, this method * returns at Promise that will resolve when the update is completely done. * * @param {Object} json The JSON description. The JSON should be in the form of an object literal, not a string. * @param {Object} [options] Object with the following properties: * @param {Boolean} [options.onlyUpdateExistingItems] true to only update existing items and never create new ones, or false is new items * may be created by this update. * @param {Boolean} [options.isUserSupplied] If specified, sets the {@link CatalogMember#isUserSupplied} property of updated catalog members * to the given value. If not specified, the property is left unchanged. * @returns {Promise} A promise that resolves when the update is complete. */ Catalog.prototype.updateFromJson = function(json, options) { var that = this; options = defaultValue(options, {}); return CatalogGroup.updateItems(json, options, this.group).then(function () { that.terria.nowViewing.sortByNowViewingIndices(); }); }; /** * Serializes the catalog to JSON. * * @param {Object} [options] Object with the following properties: * @param {Function} [options.propertyFilter] Filter function that will be executed to determine whether a property * should be serialized. * @param {Function} [options.itemFilter] Filter function that will be executed for each item in a group to determine * whether that item should be serialized. * @return {Object} The serialized JSON object-literal. */ Catalog.prototype.serializeToJson = function(options) { this.terria.nowViewing.recordNowViewingIndices(); return this.group.serializeToJson(options).items; }; /** * Resolves items in the catalog based on the share keys provided, and updates them with the passed info and * enables them along with all their ancestors in the catalog hierarchy. This is asynchronous as it may involve a number * of CatalogItem#load calls. * * Note that because of the lazily-loaded nature of the catalog, items within it may not be resolvable by shareKey until * their parents have loaded. As a result this loads sharedObjects in serial from left to right. If a catalog member is the * child of an asynchronously-loaded catalog group (like a ckan or socrata group), then that group's shareKey precede the * child member. * * @param {Object} sharedObjects A flat map of string-based share keys with data to update on the resolved object. It is * possible to pass an empty object if nothing needs updating. * @returns {Promise} A promise that will resolve when all the items have been loaded and enabled. */ Catalog.prototype.updateByShareKeys = function(sharedObjects) { const that = this; return Object.keys(sharedObjects).reduce(function(aggregatedPromise, shareKey) { var itemJson = sharedObjects[shareKey]; return aggregatedPromise .then(this._loadInSerial.bind(this, itemJson.parents || [])) .then(this._updateAndEnable.bind(this, shareKey, itemJson)); }.bind(this), when()).then(() => { that.terria.nowViewing.sortByNowViewingIndices(); } ); }; /** * Calls {@link CatalogMember#load} on a number of catalog members, identified by their share key, one after the other * from left to right. Doing this in serial is important because sometimes calling load() on a catalog group will * reveal more catalog items under it, which will be added to the catalog's shareKeyIndex - if these were done in * parallel, share keys towards the right of the array would not able to be resolved at all. * * @param shareKeys An array of catalog member share keys to use for resolving catalog members from the catalog * @returns {Promise} A promise that will be resolved when all the catalog members have either loaded or failed to load. * @private */ Catalog.prototype._loadInSerial = function(shareKeys) { return shareKeys.reduce(function(aggregatedPromise, shareKey) { return aggregatedPromise.then(function() { if (this.shareKeyIndex[shareKey]) { return this.shareKeyIndex[shareKey].load(); } }.bind(this)); }.bind(this), when()); }; /** * Finds the an item in the catalog with the passed shareKey, updates it with the passed itemJson, THEN calls load on * the item, THEN enables it and all its parents. If no item for the passed shareKey can be found, logs a warning but * proceeds without an exception. * * @returns {Promise} A promise that will be resolved when all of this is done. * @private */ Catalog.prototype._updateAndEnable = function(shareKey, itemJson) { var existingMember = this.shareKeyIndex[shareKey]; if (existingMember) { // Update THEN load is the expected behaviour for some catalog items (e.g. CSV) - if these operations aren't // executed in this order then bugs happen in auto-generated legends. return existingMember.updateFromJson(itemJson) .then(existingMember.load.bind(existingMember)) .then(existingMember.enableWithParents.bind(existingMember)); } else { console.warn('Share link has a catalog member with shareKey ' + shareKey + ' but could not find ' + 'this in the catalog'); } }; /** * If a catalog group exists with this name, update it, otherwise create it. * @param {Function} CatalogGroupConstructor The constructor function for the catalog group (typically CatalogGroup). * @param {String} name The catalog group's name. * @param {String} description The catalog group's description. * @return {CatalogGroup} The new or updated catalog group. */ Catalog.prototype.upsertCatalogGroup = function(CatalogGroupConstructor, name, description) { var group; var groups = this.group.items; for (var i = 0; i < groups.length; ++i) { group = groups[i]; if (group.name === name) { return group; } } group = new CatalogGroupConstructor(this.terria); group.name = name; group.description = description; group.isUserSupplied = true; this.group.add(group); return group; }; module.exports = Catalog;