terriajs
Version:
Geospatial data visualization platform.
285 lines (245 loc) • 9.94 kB
JavaScript
'use strict';
/*global require*/
var clone = require('terriajs-cesium/Source/Core/clone');
var defaultValue = require('terriajs-cesium/Source/Core/defaultValue');
var defined = require('terriajs-cesium/Source/Core/defined');
var defineProperties = require('terriajs-cesium/Source/Core/defineProperties');
var freezeObject = require('terriajs-cesium/Source/Core/freezeObject');
var knockout = require('terriajs-cesium/Source/ThirdParty/knockout');
var RuntimeError = require('terriajs-cesium/Source/Core/RuntimeError');
var when = require('terriajs-cesium/Source/ThirdParty/when');
var inherit = require('../Core/inherit');
var overrideProperty = require('../Core/overrideProperty');
var CatalogItem = require('./CatalogItem');
var createCatalogMemberFromType = require('./createCatalogMemberFromType');
/**
* A {@link CatalogItem} composed of multiple other catalog items. When this item is enabled or shown, the composed items are
* enabled or shown as well. Other properties, including {@link CatalogItem#rectangle},
* {@link CatalogItem#clock}, and {@link CatalogItem#legendUrls}, are not composed in any way, so you should manually set those
* properties on this object as appropriate.
*
* @alias CompositeCatalogItem
* @constructor
* @extends CatalogItem
*
* @param {Terria} terria The Terria instance.
* @param {CatalogItem[]} [items] The items to compose.
*/
var CompositeCatalogItem = function(terria, items) {
CatalogItem.call(this, terria);
this.items = defined(items) ? items.slice() : [];
knockout.track(this, ['items']);
overrideProperty(this, 'legendUrls', {
get: function() {
if (!defined(this._legendUrls) || this._legendUrls.length === 0) {
this._legendUrls = [];
for (var i = 0; i < this.items.length; ++i) {
if (this.items[i].legendUrls) {
this._legendUrls = this._legendUrls.concat(this.items[i].legendUrls);
}
}
}
return this._legendUrls;
}
});
knockout.getObservable(this, 'items').subscribe(function() {
for (var i = 0; i < this.items.length; ++i) {
this.items[i].showInNowViewingWhenEnabled = false;
}
}, this);
};
inherit(CatalogItem, CompositeCatalogItem);
defineProperties(CompositeCatalogItem.prototype, {
/**
* Gets the type of data member represented by this instance.
* @memberOf CompositeCatalogItem.prototype
* @type {String}
*/
type : {
get : function() {
return 'composite';
}
},
/**
* Gets a human-readable name for this type of data source, such as 'Web Map Service (WMS)'.
* @memberOf CompositeCatalogItem.prototype
* @type {String}
*/
typeName : {
get : function() {
return 'Composite';
}
},
/**
* Gets the set of functions used to update individual properties in {@link CatalogMember#updateFromJson}.
* When a property name in the returned object literal matches the name of a property on this instance, the value
* will be called as a function and passed a reference to this instance, a reference to the source JSON object
* literal, and the name of the property.
* @memberOf CompositeCatalogItem.prototype
* @type {Object}
*/
updaters : {
get : function() {
return CompositeCatalogItem.defaultUpdaters;
}
},
/**
* Gets the set of functions used to serialize individual properties in {@link CatalogMember#serializeToJson}.
* When a property name on the model matches the name of a property in the serializers object literal,
* the value will be called as a function and passed a reference to the model, a reference to the destination
* JSON object literal, and the name of the property.
* @memberOf CompositeCatalogItem.prototype
* @type {Object}
*/
serializers : {
get : function() {
return CompositeCatalogItem.defaultSerializers;
}
},
/**
* Gets the set of names of the properties to be serialized for this object when {@link CatalogMember#serializeToJson} is called
* for a share link.
* @memberOf CompositeCatalogItem.prototype
* @type {String[]}
*/
propertiesForSharing : {
get : function() {
return CompositeCatalogItem.defaultPropertiesForSharing;
}
},
/**
* Gets a value indicating whether this data source, when enabled, can be reordered with respect to other data sources.
* Data sources that cannot be reordered are typically displayed above reorderable data sources.
* @memberOf CsvCatalogItem.prototype
* @type {Boolean}
*/
supportsReordering : {
get : function() {
// we will use the heuristic that the composite supports reordering only
// if all its subitems do
var result = true;
for (var itemIndex = 0; itemIndex < this.items.length; ++itemIndex) {
var item = this.items[itemIndex];
result = result && item.supportsReordering;
}
return result;
}
}
});
/**
* Gets or sets the set of default updater functions to use in {@link CatalogMember#updateFromJson}. Types derived from this type
* should expose this instance - cloned and modified if necesary - through their {@link CatalogMember#updaters} property.
* @type {Object}
*/
// Adapted from CatalogGroup
CompositeCatalogItem.defaultUpdaters = clone(CatalogItem.defaultUpdaters);
CompositeCatalogItem.defaultUpdaters.items = function(compositeCatalogItem, json, propertyName, options) {
// Let the item finish loading first. Otherwise, these changes could get clobbered by the load.
return when(compositeCatalogItem.load(), function() {
var promises = [];
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
var items = json.items;
for (var itemIndex = 0; itemIndex < items.length; ++itemIndex) {
var item = items[itemIndex];
if (!defined(item.type)) {
throw new RuntimeError('Each item must have a type.');
}
if (item.type === 'composite') {
throw new RuntimeError('Composites cannot include composite items.');
}
var existingItem = createCatalogMemberFromType(item.type, compositeCatalogItem.terria);
compositeCatalogItem.add(existingItem);
promises.push(existingItem.updateFromJson(item, options));
}
return when.all(promises);
});
};
CompositeCatalogItem.defaultUpdaters.isLoading = function(compositeCatalogItem, json, propertyName) {};
freezeObject(CompositeCatalogItem.defaultUpdaters);
/**
* Gets or sets the set of default serializer functions to use in {@link CatalogMember#serializeToJson}. Types derived from this type
* should expose this instance - cloned and modified if necesary - through their {@link CatalogMember#serializers} property.
* @type {Object}
*/
CompositeCatalogItem.defaultSerializers = clone(CatalogItem.defaultSerializers);
CompositeCatalogItem.defaultSerializers.items = function(compositeCatalogItem, json, propertyName, options) {
var items = json.items = [];
for (var i = 0; i < compositeCatalogItem.items.length; ++i) {
var item = compositeCatalogItem.items[i].serializeToJson(options);
if (defined(item)) {
items.push(item);
}
}
};
CompositeCatalogItem.defaultSerializers.isLoading = function(compositeCatalogItem, json, propertyName, options) {};
freezeObject(CompositeCatalogItem.defaultSerializers);
/**
* Gets or sets the default set of properties that are serialized when serializing a {@link CatalogItem}-derived object
* for a share link.
* @type {String[]}
*/
CompositeCatalogItem.defaultPropertiesForSharing = clone(CatalogItem.defaultPropertiesForSharing);
CompositeCatalogItem.defaultPropertiesForSharing.push('items');
freezeObject(CompositeCatalogItem.defaultPropertiesForSharing);
//
CompositeCatalogItem.prototype._load = function() {
return when.all(
this.items.map(function(item) {
return item.load();
})
);
};
CompositeCatalogItem.prototype._getValuesThatInfluenceLoad = function() {
var result = [];
for (var i = 0; i < this.items.length; ++i) {
result.push.apply(result, this.items[i]._getValuesThatInfluenceLoad());
}
return result;
};
CompositeCatalogItem.prototype._enable = function() {
var i;
try {
for (i = 0; i < this.items.length; ++i) {
this.items[i]._enable();
}
} catch(e) {
for (var j = 0; j < i; ++j) {
this.items[j]._disable();
}
this.isEnabled = false;
throw e;
}
};
CompositeCatalogItem.prototype._disable = function() {
for (var i = 0; i < this.items.length; ++i) {
this.items[i]._disable();
}
};
CompositeCatalogItem.prototype._show = function() {
var i;
try {
for (i = 0; i < this.items.length; ++i) {
this.items[i]._show();
}
} catch(e) {
for (var j = 0; j < i; ++j) {
this.items[j]._hide();
}
this.isShown = false;
throw e;
}
};
CompositeCatalogItem.prototype._hide = function() {
for (var i = 0; i < this.items.length; ++i) {
this.items[i]._hide();
}
};
/**
* Adds an item or group to this composite.
*
* @param {CatalogMember} item The item to add.
*/
CompositeCatalogItem.prototype.add = function(item) {
this.items.push(item);
};
module.exports = CompositeCatalogItem;