Skip to content

Commit

Permalink
Merge pull request #8103 from CartoDB/7742-filter-and-intersection-an…
Browse files Browse the repository at this point in the history
…alysis

Adds filter analysis
  • Loading branch information
javierarce committed Jun 23, 2016
2 parents d43606b + af95ea0 commit 5caa289
Show file tree
Hide file tree
Showing 11 changed files with 361 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ module.exports = function (alsoGenerateFromCamshaftReference) {
},
title: _t('components.modals.add-analysis.option-types.trade-area.title'),
desc: _t('components.modals.add-analysis.option-types.trade-area.desc')
}, {
nodeAttrs: {
type: 'filter-range'
},
title: _t('components.modals.add-analysis.option-types.filter.title'),
type_group: _t('analyses.filter'),
desc: _t('components.modals.add-analysis.option-types.filter.desc')
}
]
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ module.exports = function (type) {
case 'buffer': return _t('analyses.area-of-influence');
case 'trade-area': return _t('analyses.area-of-influence');
case 'point-in-polygon': return _t('analyses.point-in-polygon');
case 'filter-range': return _t('analyses.filter');
case 'filter-category': return _t('analyses.filter');
default: return type;
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
var _ = require('underscore');

var PARAMS_FOR_IS_EQUAL = 'kind,text';

module.exports = {
type: 'filter-category',
label: 'Filter category',
parametersDataFields: 'text,accept_reject',
filterDataFields: 'accept,reject',

parse: function (nodeAttrs) {
var formAttrs = _.extend({}, nodeAttrs);

formAttrs.type = 'filter-category';
formAttrs.column = nodeAttrs.column || '';

if (nodeAttrs.accept) {
formAttrs.text = nodeAttrs.accept.join(',');
} else if (nodeAttrs.reject) {
formAttrs.text = nodeAttrs.reject.join(',');
}

formAttrs.accept_reject = (nodeAttrs.accept || !(nodeAttrs.accept || nodeAttrs.reject)) ? 'accept' : 'reject';

return formAttrs;
},

formatAttrs: function (formAttrs) {
var attrs = ['id', 'type', 'source', 'column'];
var isFromIsEqualToKind = formAttrs.kind === 'is-equal-to';
var text;

if (isFromIsEqualToKind) {
text = [+formAttrs.text];
} else if (formAttrs.text) {
text = formAttrs.text.split(',');
}

if (formAttrs.accept_reject === 'accept' || isFromIsEqualToKind) {
formAttrs.accept = text;
return _.pick(formAttrs, attrs.concat('accept'));
} else if (formAttrs.accept_reject === 'reject') {
formAttrs.reject = text;
return _.pick(formAttrs, attrs.concat('reject'));
}
},

getParameters: function (kind) {
var params = this.parametersDataFields;

if (kind === 'is-equal-to') {
params = PARAMS_FOR_IS_EQUAL;
}

return params;
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
var _ = require('underscore');
var BaseAnalysisFormModel = require('./base-analysis-form-model');
var template = require('./filter-form.tpl');
var ColumnOptions = require('../column-options');

var FILTER_TYPES = require('./filter-types');
var TYPE_TO_META_MAP = {};

FILTER_TYPES.map(function (d) {
TYPE_TO_META_MAP[d.type] = d;
});

var FILTER_RANGE_KINDS = [
{ val: 'between', label: _t('editor.layers.filter-options.between') },
{ val: 'is-equal-to', label: _t('editor.layers.filter-options.is-equal-to') },
{ val: 'is-greater-than', label: _t('editor.layers.filter-options.is-greater-than') },
{ val: 'is-less-than', label: _t('editor.layers.filter-options.is-less-than') }
];

module.exports = BaseAnalysisFormModel.extend({

parse: function (attrs) {
return _.defaults(
_.pick(attrs, 'id', 'source', 'column'), // maintain default attrs
this._typeDef(attrs.type).parse(attrs)
);
},

initialize: function () {
BaseAnalysisFormModel.prototype.initialize.apply(this, arguments);

var nodeDefModel = this._layerDefinitionModel.findAnalysisDefinitionNodeModel(this.get('source'));

this._columnOptions = new ColumnOptions({}, {
nodeDefModel: nodeDefModel
});

this.on('change:kind change:column', this._updateSchema, this);
this.listenTo(this._columnOptions, 'columnsFetched', this._setSchema);

this._setSchema();
},

getTemplate: function () {
return template;
},

getTemplateData: function () {
var p = this._typeDef().getParameters(this.get('kind'));
return { parametersDataFields: p };
},

_formatAttrs: function (formAttrs) {
var customFormattedFormAttrs = this._typeDef().formatAttrs(formAttrs);
return BaseAnalysisFormModel.prototype._formatAttrs.call(this, customFormattedFormAttrs);
},

/**
* @override {BaseAnalysisFormModel._updateNodeDefinition}
*/
_updateNodeDefinition: function (nodeDefModel) {
var attrs = this._formatAttrs(this.attributes);
nodeDefModel.clear({ silent: true });
nodeDefModel.set(attrs);
},

_setSchema: function () {
BaseAnalysisFormModel.prototype._setSchema.call(this, this._filterSchemaFieldsByType({
source: {
type: 'Select',
text: _t('editor.layers.analysis-form.input'),
options: [ this.get('source') ],
editorAttrs: { disabled: true }
},
column: {
type: 'Select',
text: _t('editor.layers.analysis-form.column'),
options: this._columnOptions.filterByType(['string', 'number']),
validators: ['required']
},
kind: {
type: 'Select',
title: _t('editor.layers.analysis-form.filter'),
options: this._getKindOptions(),
editorAttrs: {
showSearch: false
}
},
min: {
type: 'Text',
title: _t('editor.layers.analysis-form.min'),
validators: ['required']
},
max: {
type: 'Text',
title: _t('editor.layers.analysis-form.max'),
validators: ['required']
},
text: {
type: 'Text',
title: _t('editor.layers.analysis-form.input'),
validators: ['required']
},
accept_reject: {
type: 'Radio',
title: _t('editor.layers.analysis-form.result'),
options: [
{ label: _t('editor.layers.analysis-form.show'), val: 'accept' },
{ label: _t('editor.layers.analysis-form.hide'), val: 'reject' }
],
validators: ['required']
}
}));
},

_getSourceColumns: function () {
var nodeDefModel = this._layerDefinitionModel.findAnalysisDefinitionNodeModel(this.get('source'));

var sourceColumns = nodeDefModel.querySchemaModel.columnsCollection.map(function (columnModel) {
var columnName = columnModel.get('name');
return {
val: columnName,
label: columnName,
type: columnModel.get('type')
};
});

return sourceColumns;
},

_filterSchemaFieldsByType: function (schema) {
// Always include the source and column fields in addition to the type-specific fields
var kind = this.get('kind');
var fields = ['source', 'column'].concat(this._typeDef().getParameters(kind).split(','));
return _.pick(schema, fields);
},

_updateSchema: function () {
this._setType();
this._setSchema();
},

_getSelectedColumnType: function () {
var columns = this._getSourceColumns();
var columnType = null;

for (var i in columns) {
if (columns[i]['label'] === this.get('column')) {
columnType = columns[i]['type'];
}
}

return columnType;
},

_getKindOptions: function () {
if (this.get('type') !== 'filter-category' || this.get('kind') === 'is-equal-to') {
return FILTER_RANGE_KINDS;
}
},

_setType: function () {
var columnType = this._getSelectedColumnType();
var kind = this.get('kind');
var attrs = {
type: 'filter-range'
};

if (kind === 'is-equal-to') {
attrs.type = 'filter-category';
attrs.accept_reject = 'accept';
} else if (columnType === 'string') {
attrs.type = 'filter-category';
}

this.set(attrs);
},

_typeDef: function (type) {
type = type || this.get('type');
return TYPE_TO_META_MAP[type];
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<form>
<div class="Editor-HeaderInfo">
<div class="Editor-HeaderNumeration CDB-Text is-semibold u-rSpace--m">2</div>
<div class="Editor-HeaderInfo-inner CDB-Text">
<div class="Editor-HeaderInfo-title u-bSpace--m">
<h2 class="CDB-Text CDB-HeaderInfo-titleText CDB-Size-large"><%- _t('analyses.filter') %></h2>
</div>
<p class="CDB-Text u-upperCase CDB-FontSize-small u-altTextColor u-bSpace--m"><%- _t('editor.layers.analysis-form.select-column') %></p>
<div class="u-tSpace-xl CDB-Text CDB-Fieldset">
<p class="CDB-Legend u-upperCase u-iBlock CDB-Text is-semibold CDB-Size-small u-rSpace--m"><%- _t('editor.layers.analysis-form.input') %></p>
<div class="Editor-formInput" data-editors="source"></div>
</div>
<div class="u-tSpace-xl CDB-Text CDB-Fieldset">
<p class="CDB-Legend u-upperCase u-iBlock CDB-Text is-semibold CDB-Size-small u-rSpace--m"><%- _t('editor.layers.analysis-form.column') %></p>
<div class="Editor-formInput" data-editors="column"></div>
</div>
</div>
</div>
<div class="Editor-HeaderInfo">
<div class="Editor-HeaderNumeration CDB-Text is-semibold u-rSpace--m">3</div>
<div class="Editor-HeaderInfo-inner CDB-Text" data-fields="<%- parametersDataFields %>">
<div class="Editor-HeaderInfo-title u-bSpace--m">
<h2 class="CDB-Text CDB-HeaderInfo-titleText CDB-Size-large"><%- _t('editor.layers.analysis-form.parameters') %></h2>
</div>
<p class="CDB-Text u-upperCase CDB-FontSize-small u-altTextColor u-bSpace--xl"><%- _t('editor.layers.analysis-form.parameters-description') %></p>
</div>
</div>
</form>
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
var _ = require('underscore');

var PARAMETERS_FOR_KIND = {
'between': 'kind,min,max',
'is-equal-to': 'kind,text',
'is-less-than': 'kind,max',
'is-greater-than': 'kind,min'
};

module.exports = {
type: 'filter-range',
label: 'Filter range', // TODO: localize?

parse: function (nodeAttrs) {
var formAttrs = _.extend({}, nodeAttrs);

formAttrs.type = 'filter-range';
formAttrs.kind = nodeAttrs.kind || 'between';
formAttrs.column = nodeAttrs.column || '';
formAttrs.min = nodeAttrs.min || 0;
formAttrs.max = nodeAttrs.max || 50;

return formAttrs;
},

getParameters: function (kind) {
if (!kind) {
kind = 'is-equal-to';
}
return PARAMETERS_FOR_KIND[kind];
},

formatAttrs: function (formAttrs) {
var attrs = this.getParameters(formAttrs.kind);
return _.pick(formAttrs, ['id', 'type', 'source', 'column'].concat(attrs.split(',')));
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = [
require('./filter-category'),
require('./filter-range')
];
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,29 @@ var _ = require('underscore');
var Backbone = require('backbone');

var AreaOfInfluenceFormModel = require('./analysis-form-models/area-of-influence-form-model');
var WeightedCentroidModelFormModel = require('./analysis-form-models/weighted-centroid-form-model');
var FallbackFormModel = require('./analysis-form-models/fallback-form-model');
var FilterByNodeColumn = require('./analysis-form-models/filter-by-node-column');
var FilterFormModel = require('./analysis-form-models/filter-form-model');
var FilterRangeModel = require('./analysis-form-models/filter-form-model');
var IntersectionFormModel = require('./analysis-form-models/intersection-form-model');
var KmeansFormModel = require('./analysis-form-models/kmeans-form-model');
var MoranFormModel = require('./analysis-form-models/moran-form-model');
var FallbackFormModel = require('./analysis-form-models/fallback-form-model');
var PointInPolygonFormModel = require('./analysis-form-models/point-in-polygon-form-model');
var IntersectionFormModel = require('./analysis-form-models/intersection-form-model');
var UnknownTypeFormModel = require('./analysis-form-models/unknown-type-form-model');
var FilterByNodeColumn = require('./analysis-form-models/filter-by-node-column');
var WeightedCentroidModelFormModel = require('./analysis-form-models/weighted-centroid-form-model');

var TYPE_TO_ANALYSIS_FORM_MODELS_MAP = {
'aggregate-intersection': IntersectionFormModel,
'buffer': AreaOfInfluenceFormModel,
'trade-area': AreaOfInfluenceFormModel,
'filter-by-node-column': FilterByNodeColumn,
'filter-category': FilterFormModel,
'filter-range': FilterRangeModel,
'intersection': IntersectionFormModel,
'kmeans': KmeansFormModel,
'moran': MoranFormModel,
'weighted-centroid': WeightedCentroidModelFormModel,
'point-in-polygon': PointInPolygonFormModel,
'intersection': IntersectionFormModel,
'aggregate-intersection': IntersectionFormModel,
'filter-by-node-column': FilterByNodeColumn
'trade-area': AreaOfInfluenceFormModel,
'weighted-centroid': WeightedCentroidModelFormModel
};

module.exports = Backbone.Collection.extend({
Expand Down Expand Up @@ -89,5 +93,4 @@ module.exports = Backbone.Collection.extend({
this._walkAnalysisChain(next, layerDefModel, visitorFn);
}
}

});
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
var _ = require('underscore');
var Backbone = require('backbone');

module.exports = Backbone.Model.extend({
Expand Down Expand Up @@ -34,7 +35,11 @@ module.exports = Backbone.Model.extend({
};
});

if (columnType) {
if (_.isArray(columnType)) {
columns = columns.filter(function (column) {
return _.contains(columnType, column.type);
});
} else if (columnType) {
columns = columns.filter(function (column) {
return column.type === columnType;
});
Expand Down
Loading

0 comments on commit 5caa289

Please sign in to comment.