/** * @fileoverview Collection filters. * * new Filter('time', low, high) * .bounds(ring) * .eq('time', value) * .lt('time', value) */ goog.provide('ee.Filter'); goog.require('ee.ApiFunction'); goog.require('ee.ComputedObject'); goog.require('goog.array'); goog.require('goog.string'); /** * Constructs a new filter. This constuctor accepts the following args: * - Another filter. * - An array of filters (which are implicitly ANDed together). * - A ComputedObject returning a filter. Users shouldn't be making these; * they're produced by the generator functions below. * * @param {ee.Filter|Array.<*>|Object=} opt_filter Optional filter to add. * @constructor * @extends {ee.ComputedObject} */ ee.Filter = function(opt_filter) { // Constructor safety. if (!(this instanceof ee.Filter)) { return new ee.Filter(opt_filter); } else if (opt_filter instanceof ee.Filter) { // If we've been passed another filter, just return it. return opt_filter; } ee.Filter.initialize(); /** * The internal rerpresentation of this filter. This is * an array of filter objects which are implicitly ANDed together. * * @type {Array.<*>} * @private */ this.filter_; if (goog.isArray(opt_filter)) { if (opt_filter.length == 0) { throw Error('Empty list specified for ee.Filter().'); } else if (opt_filter.length == 1) { return new ee.Filter(opt_filter[0]); } else { // AND filters together. goog.base(this, new ee.ApiFunction('Filter.and'), { 'filters': opt_filter }); this.filter_ = opt_filter; } } else if (opt_filter instanceof ee.ComputedObject) { // Actual filter object. goog.base(this, opt_filter.func, opt_filter.args); this.filter_ = [opt_filter]; } else if (!goog.isDef(opt_filter)) { // A silly call with no arguments left for backward-compatibility. // Encoding such a filter is expected to fail, but it can be composed // by calling the various methods that end up in append_(). goog.base(this, null, null); this.filter_ = []; } else { throw Error('Invalid argument specified for ee.Filter(): ' + opt_filter); } }; goog.inherits(ee.Filter, ee.ComputedObject); /** * Whether the class has been initialized with API functions. * @type {boolean} * @private */ ee.Filter.initialized_ = false; /** Imports API functions to this class. */ ee.Filter.initialize = function() { if (!ee.Filter.initialized_) { ee.ApiFunction.importApi(ee.Filter, 'Filter', 'Filter'); ee.Filter.initialized_ = true; } }; /** Removes imported API functions from this class. */ ee.Filter.reset = function() { ee.ApiFunction.clearApi(ee.Filter); ee.Filter.initialized_ = false; }; /** * A map from the deprecated old-style comparison operator names to API * function names, implicitly prefixed with "Filter.". Negative operators * (those starting with "not_") are not included. * @type {Object.} * @const * @private */ ee.Filter.functionNames_ = { 'equals': 'equals', 'less_than': 'lessThan', 'greater_than': 'greaterThan', 'contains': 'stringContains', 'starts_with': 'stringStartsWith', 'ends_with': 'stringEndsWith' }; /** * @return {number} The number of predicates that have been added to this * filter. This does not count nested predicates. */ ee.Filter.prototype.length = function() { return this.filter_.length; }; /** * Append a predicate to a filter. These are implicitly ANDed. * * @param {ee.Filter|Array.|Object} newFilter The filter * to append. Possible types are: * - another fully constructed ee.Filter, * - a ComputedObject producing a filter, * - an array of 1 or 2. * @return {ee.Filter} A new filter that is the combination of both. * @private */ ee.Filter.prototype.append_ = function(newFilter) { // Make a copy of the previous filter. var prev = this.filter_.slice(0); if (newFilter instanceof ee.Filter) { goog.array.extend(prev, newFilter.filter_); } else if (newFilter instanceof Array) { goog.array.extend(prev, newFilter); } else { prev.push(newFilter); } return new ee.Filter(prev); }; /** * Returns the opposite of this filter, i.e. a filter that will match iff * this filter doesn't. * @return {ee.Filter} The negated filter. */ ee.Filter.prototype.not = function() { return /** @type {ee.Filter} */(ee.ApiFunction._call('Filter.not', this)); }; /** * Filter on metadata. This is deprecated. * * @param {string} name The property name to filter on. * @param {string} operator The type of comparison. One of: * "equals", "less_than", "greater_than", "contains", "begins_with", * "ends_with", or any of these prefixed with "not_". * @param {*} value The value to compare against. * @return {ee.Filter} The constructed filter. */ ee.Filter.metadata = function(name, operator, value) { operator = operator.toLowerCase(); // Check for negated filters. var negated = false; if (goog.string.startsWith(operator, 'not_')) { negated = true; operator = operator.substring(4); } // Convert the operator to a function. if (!(operator in ee.Filter.functionNames_)) { throw Error('Unknown filtering operator: ' + operator); } var funcName = 'Filter.' + ee.Filter.functionNames_[operator]; var filter = /** @type {ee.Filter} */( ee.ApiFunction._call(funcName, name, value)); return negated ? filter.not() : filter; }; /** * Filter to metadata equal to the given value. * * @param {string} name The property name to filter on. * @param {*} value The value to compare against. * @return {ee.Filter} The constructed filter. */ ee.Filter.eq = function(name, value) { return /** @type {ee.Filter} */( ee.ApiFunction._call('Filter.equals', name, value)); }; /** * Filter to metadata not equal to the given value. * * @param {string} name The property name to filter on. * @param {*} value The value to compare against. * @return {ee.Filter} The constructed filter. */ ee.Filter.neq = function(name, value) { return ee.Filter.eq(name, value).not(); }; /** * Filter to metadata less than the given value. * * @param {string} name The property name to filter on. * @param {*} value The value to compare against. * @return {ee.Filter} The constructed filter. */ ee.Filter.lt = function(name, value) { return /** @type {ee.Filter} */( ee.ApiFunction._call('Filter.lessThan', name, value)); }; /** * Filter on metadata greater than or equal to the given value. * * @param {string} name The property name to filter on. * @param {*} value The value to compare against. * @return {ee.Filter} The constructed filter. */ ee.Filter.gte = function(name, value) { return ee.Filter.lt(name, value).not(); }; /** * Filter on metadata greater than the given value. * * @param {string} name The property name to filter on. * @param {*} value The value to compare against. * @return {ee.Filter} The constructed filter. */ ee.Filter.gt = function(name, value) { return /** @type {ee.Filter} */( ee.ApiFunction._call('Filter.greaterThan', name, value)); }; /** * Filter on metadata less than or equal to the given value. * * @param {string} name The property name to filter on. * @param {*} value The value to compare against. * @return {ee.Filter} The constructed filter. */ ee.Filter.lte = function(name, value) { return ee.Filter.gt(name, value).not(); }; /** * Filter on metadata containing the given string. * * @param {string} name The property name to filter on. * @param {string} value The value to compare against. * @return {ee.Filter} The constructed filter. */ ee.Filter.contains = function(name, value) { return /** @type {ee.Filter} */( ee.ApiFunction._call('Filter.stringContains', name, value)); }; /** * Filter on metadata not containing the given string. * * @param {string} name The property name to filter on. * @param {string} value The value to compare against. * @return {ee.Filter} The constructed filter. */ ee.Filter.not_contains = function(name, value) { return ee.Filter.contains(name, value).not(); }; /** * Filter on metadata begining with the given string. * * @param {string} name The property name to filter on. * @param {string} value The value to compare against. * @return {ee.Filter} The constructed filter. */ ee.Filter.starts_with = function(name, value) { return /** @type {ee.Filter} */( ee.ApiFunction._call('Filter.stringStartsWith', name, value)); }; /** * Filter on metadata not begining with the given string. * * @param {string} name The property name to filter on. * @param {string} value The value to compare against. * @return {ee.Filter} The constructed filter. */ ee.Filter.not_starts_with = function(name, value) { return ee.Filter.starts_with(name, value).not(); }; /** * Filter on metadata ending with the given string. * * @param {string} name The property name to filter on. * @param {string} value The value to compare against. * @return {ee.Filter} The constructed filter. */ ee.Filter.ends_with = function(name, value) { return /** @type {ee.Filter} */( ee.ApiFunction._call('Filter.stringEndsWith', name, value)); }; /** * Filter on metadata not ending with the given string. * * @param {string} name The property name to filter on. * @param {string} value The value to compare against. * @return {ee.Filter} The constructed filter. */ ee.Filter.not_ends_with = function(name, value) { return ee.Filter.ends_with(name, value).not(); }; /** * Combine two or more filters using boolean AND. * * @param {...ee.Filter} var_args The filters to combine. * @return {ee.Filter} The constructed filter. */ ee.Filter.and = function(var_args) { var args = Array.prototype.slice.call(arguments); return /** @type {ee.Filter} */(ee.ApiFunction._call('Filter.and', args)); }; /** * Combine two or more filters using boolean OR. * * @param {...ee.Filter} var_args The filters to combine. * @return {ee.Filter} The constructed filter. */ ee.Filter.or = function(var_args) { var args = Array.prototype.slice.call(arguments); return /** @type {ee.Filter} */(ee.ApiFunction._call('Filter.or', args)); }; /** * Filter images by date. * * @param {Date|string|number} start The start date. * @param {Date|string|number=} opt_end The end date. * @return {ee.Filter} The constructed filter. */ ee.Filter.date = function(start, opt_end) { if (!goog.isDef(opt_end)) { // Can't create half-open DateRanges. Hack around it. opt_end = 0x0000FFFFFFFFFFFF; // 8000+ years gotta be enough. } var range = ee.ApiFunction._call('DateRange', start, opt_end); var filter = ee.ApiFunction._apply('Filter.dateRangeContains', { 'leftValue': range, 'rightField': 'system:time_start' }); return /** @type {ee.Filter} */(filter); }; /** * Filter on metadata contained in a list. * * @param {string=} opt_leftField A selector for the left operand. * Should not be specified if leftValue is specified. * @param {Array|Object=} opt_rightValue The value of the right operand. * Should not be specified if rightField is specified. * @param {string=} opt_rightField A selector for the right operand. * Should not be specified if rightValue is specified. * @param {Array|Object=} opt_leftValue The value of the left operand. * Should not be specified if leftField is specified. * @return {ee.Filter} The constructed filter. */ ee.Filter.inList = function( opt_leftField, opt_rightValue, opt_rightField, opt_leftValue) { // Implement this in terms of listContains, with the arguments switched. // In listContains the list is on the left side, while in inList it's on // the right. var filter = ee.ApiFunction._apply('Filter.listContains', { 'leftField': opt_rightField, 'rightValue': opt_leftValue, 'rightField': opt_leftField, 'leftValue': opt_rightValue }); return /** @type {ee.Filter} */(filter); }; /** * Filter on bounds. * * @param {ee.Geometry|ee.ComputedObject|ee.FeatureCollection} geometry * The geometry, feature or collection to filter to. * @param {number|ee.ComputedObject=} opt_errorMargin An optional error margin. * If a number, interpreted as sphere surface meters. * @return {ee.Filter} The modified filter. */ ee.Filter.bounds = function(geometry, opt_errorMargin) { // Invoke geometry promotion then manually promote to a Feature. // TODO(user): Discuss whether filters should go back to working // directly on geometries. return /** @type {ee.Filter} */( ee.ApiFunction._apply('Filter.intersects', { 'leftField': '.all', 'rightValue': ee.ApiFunction._call('Feature', geometry), 'maxError': opt_errorMargin })); }; /** * Prototyped versions of all filter operators. */ /** * @see ee.Filter.eq * @return {ee.Filter} The modified filter. */ ee.Filter.prototype.eq = function() { return this.append_(ee.Filter.eq.apply(null, [].slice.call(arguments))); }; /** * @see ee.Filter.neq * @return {ee.Filter} The modified filter. */ ee.Filter.prototype.neq = function() { return this.append_(ee.Filter.neq.apply(null, [].slice.call(arguments))); }; /** * @see ee.Filter.lt * @return {ee.Filter} The modified filter. */ ee.Filter.prototype.lt = function() { return this.append_(ee.Filter.lt.apply(null, [].slice.call(arguments))); }; /** * @see ee.Filter.gte * @return {ee.Filter} The modified filter. */ ee.Filter.prototype.gte = function() { return this.append_(ee.Filter.gte.apply(null, [].slice.call(arguments))); }; /** * @see ee.Filter.gt * @return {ee.Filter} The modified filter. */ ee.Filter.prototype.gt = function() { return this.append_(ee.Filter.gt.apply(null, [].slice.call(arguments))); }; /** * @see ee.Filter.lte * @return {ee.Filter} The modified filter. */ ee.Filter.prototype.lte = function() { return this.append_(ee.Filter.lte.apply(null, [].slice.call(arguments))); }; /** * @see ee.Filter.contains * @return {ee.Filter} The modified filter. */ ee.Filter.prototype.contains = function() { return this.append_(ee.Filter.contains.apply(null, [].slice.call(arguments))); }; /** * @see ee.Filter.not_contains * @return {ee.Filter} The modified filter. */ ee.Filter.prototype.not_contains = function() { return this.append_( ee.Filter.not_contains.apply(null, [].slice.call(arguments))); }; /** * @see ee.Filter.starts_with * @return {ee.Filter} The modified filter. */ ee.Filter.prototype.starts_with = function() { return this.append_( ee.Filter.starts_with.apply(null, [].slice.call(arguments))); }; /** * @see ee.Filter.not_starts_with * @return {ee.Filter} The modified filter. */ ee.Filter.prototype.not_starts_with = function() { return this.append_( ee.Filter.not_starts_with.apply(null, [].slice.call(arguments))); }; /** * @see ee.Filter.ends_with * @return {ee.Filter} The modified filter. */ ee.Filter.prototype.ends_with = function() { return this.append_( ee.Filter.ends_with.apply(null, [].slice.call(arguments))); }; /** * @see ee.Filter.not_ends_with * @return {ee.Filter} The modified filter. */ ee.Filter.prototype.not_ends_with = function() { return this.append_( ee.Filter.not_ends_with.apply(null, [].slice.call(arguments))); }; /** * @see ee.Filter.and * @return {ee.Filter} The modified filter. */ ee.Filter.prototype.and = function() { return this.append_(ee.Filter.and.apply(null, [].slice.call(arguments))); }; /* * There is no prototype version of 'or', to avoid the abiguous syntax. * Example: a.or(b), doesn't do what it looks like it would; it appends * "or(b)" to the list of filters that includes a, which is equal * to "and(a, b)", not "or(a,b)". */ /** * @see ee.Filter.date * @return {ee.Filter} The modified filter. */ ee.Filter.prototype.date = function() { return this.append_(ee.Filter.date.apply(null, [].slice.call(arguments))); }; /** * @see ee.Filter.inList * @return {ee.Filter} The modified filter. */ ee.Filter.prototype.inList = function() { return this.append_(ee.Filter.inList.apply(null, [].slice.call(arguments))); }; /** * @see ee.Filter.bounds * @return {ee.Filter} The modified filter. */ ee.Filter.prototype.bounds = function() { return this.append_(ee.Filter.bounds.apply(null, [].slice.call(arguments))); }; /** * @return {string} The name of the object, used in toString(). * @protected */ ee.Filter.prototype.name = function() { return 'Filter'; }; // Exported symbols goog.exportSymbol('ee.Filter', ee.Filter); goog.exportProperty(ee.Filter.prototype, 'length', ee.Filter.prototype.length); goog.exportProperty(ee.Filter.prototype, 'and', ee.Filter.prototype.and); goog.exportProperty(ee.Filter.prototype, 'eq', ee.Filter.prototype.eq); goog.exportProperty(ee.Filter.prototype, 'neq', ee.Filter.prototype.neq); goog.exportProperty(ee.Filter.prototype, 'lt', ee.Filter.prototype.lt); goog.exportProperty(ee.Filter.prototype, 'gte', ee.Filter.prototype.gte); goog.exportProperty(ee.Filter.prototype, 'gt', ee.Filter.prototype.gt); goog.exportProperty(ee.Filter.prototype, 'lte', ee.Filter.prototype.lte); goog.exportProperty(ee.Filter.prototype, 'contains', ee.Filter.prototype.contains); goog.exportProperty(ee.Filter.prototype, 'not_contains', ee.Filter.prototype.not_contains); goog.exportProperty(ee.Filter.prototype, 'starts_with', ee.Filter.prototype.starts_with); goog.exportProperty(ee.Filter.prototype, 'not_starts_with', ee.Filter.prototype.not_starts_with); goog.exportProperty(ee.Filter.prototype, 'ends_with', ee.Filter.prototype.ends_with); goog.exportProperty(ee.Filter.prototype, 'not_ends_with', ee.Filter.prototype.not_ends_with); goog.exportProperty(ee.Filter.prototype, 'bounds', ee.Filter.prototype.bounds); goog.exportProperty(ee.Filter.prototype, 'inList', ee.Filter.prototype.inList); goog.exportProperty(ee.Filter.prototype, 'date', ee.Filter.prototype.date); goog.exportProperty(ee.Filter.prototype, 'serialize', ee.Filter.prototype.serialize); goog.exportProperty(ee.Filter.prototype, 'toString', ee.Filter.prototype.toString); // Static versions. goog.exportProperty(ee.Filter, 'and', ee.Filter.and); goog.exportProperty(ee.Filter, 'or', ee.Filter.or); goog.exportProperty(ee.Filter, 'eq', ee.Filter.eq); goog.exportProperty(ee.Filter, 'neq', ee.Filter.neq); goog.exportProperty(ee.Filter, 'lt', ee.Filter.lt); goog.exportProperty(ee.Filter, 'gte', ee.Filter.gte); goog.exportProperty(ee.Filter, 'gt', ee.Filter.gt); goog.exportProperty(ee.Filter, 'lte', ee.Filter.lte); goog.exportProperty(ee.Filter, 'contains', ee.Filter.contains); goog.exportProperty(ee.Filter, 'not_contains', ee.Filter.not_contains); goog.exportProperty(ee.Filter, 'starts_with', ee.Filter.starts_with); goog.exportProperty(ee.Filter, 'not_starts_with', ee.Filter.not_starts_with); goog.exportProperty(ee.Filter, 'ends_with', ee.Filter.ends_with); goog.exportProperty(ee.Filter, 'not_ends_with', ee.Filter.not_ends_with); goog.exportProperty(ee.Filter, 'bounds', ee.Filter.bounds); goog.exportProperty(ee.Filter, 'inList', ee.Filter.inList); goog.exportProperty(ee.Filter, 'date', ee.Filter.date);