diff --git a/lib/carto/renderer_js.js b/lib/carto/renderer_js.js index 906086b..9e024ed 100644 --- a/lib/carto/renderer_js.js +++ b/lib/carto/renderer_js.js @@ -229,6 +229,8 @@ CartoCSS.prototype = { // serach the max index to know rendering order lyr.index = _.max(props[v].map(function(a) { return a.index; }).concat(lyr.index)); lyr.constant = !_.any(props[v].map(function(a) { return !a.constant; })); + // True when the property is filtered. + lyr.filtered = props[v][0].filtered; } } diff --git a/lib/carto/tree/definition.js b/lib/carto/tree/definition.js index 2ba7fc7..8483e63 100644 --- a/lib/carto/tree/definition.js +++ b/lib/carto/tree/definition.js @@ -210,48 +210,37 @@ tree.Definition.prototype.toXML = function(env, existing) { tree.Definition.prototype.toJS = function(env) { var shaderAttrs = {}; - - // merge conditions from filters with zoom condition of the - // definition - var zoom = "(" + this.zoom + " & (1 << ctx.zoom))"; var frame_offset = this.frame_offset; - var _if = this.filters.toJS(env); - var filters = [zoom]; - if(_if) filters.push(_if); - if(frame_offset) filters.push('ctx["frame-offset"] === ' + frame_offset); - _if = filters.join(" && "); - _.each(this.rules, function(rule) { - if(rule instanceof tree.Rule) { - shaderAttrs[rule.name] = shaderAttrs[rule.name] || []; - - var r = { - index: rule.index, - symbolizer: rule.symbolizer - }; - - if (_if) { - r.js = "if(" + _if + "){" + rule.value.toJS(env) + "}" - } else { - r.js = rule.value.toJS(env); - } + var zoomFilter = "(" + this.zoom + " & (1 << ctx.zoom))"; + var filters = [zoomFilter]; + var originalFilters = this.filters.toJS(env); + + + if (originalFilters) { + filters.push(originalFilters); + } + + if (frame_offset) { + filters.push('ctx["frame-offset"] === ' + frame_offset); + } - r.constant = rule.value.ev(env).is !== 'field'; - r.filtered = !!_if; - - shaderAttrs[rule.name].push(r); - } else { - throw new Error("Ruleset not supported"); - //if (rule instanceof tree.Ruleset) { - //var sh = rule.toJS(env); - //for(var v in sh) { - //shaderAttrs[v] = shaderAttrs[v] || []; - //for(var attr in sh[v]) { - //shaderAttrs[v].push(sh[v][attr]); - //} - //} - //} + _.each(this.rules, function (rule) { + var exportedRule = {}; + + if (!rule instanceof tree.Rule) { + throw new Error("Ruleset not supported"); } + + exportedRule.index = rule.index; + exportedRule.symbolizer = rule.symbolizer; + exportedRule.js = "if(" + filters.join(" && ") + "){" + rule.value.toJS(env) + "}"; + exportedRule.constant = rule.value.ev(env).is !== 'field'; + exportedRule.filtered = originalFilters !== ''; + + shaderAttrs[rule.name] = shaderAttrs[rule.name] || []; + shaderAttrs[rule.name].push(exportedRule); }); + return shaderAttrs; }; diff --git a/test/filtered.test.js b/test/filtered.test.js new file mode 100644 index 0000000..caa6166 --- /dev/null +++ b/test/filtered.test.js @@ -0,0 +1,88 @@ +/** + * Test the filtered field. + * + * When compiled, a rule provides metainformation fields like index, constant...etc + * one of this fields is the "filtered field". + * + * This field gives information about whether a property is filtered or not. + * + * A property is filtered if it was activated inside a filter. In the following cartocss + * code marker-color.filtered will be true because is inside a population filter. + * + * #layer { + * maker-width: 20; + * [population > 100] { + * marker-color: red; // + * } + * } + */ +var assert = require('assert'); +var Carto = require('../lib/carto/index.js'); +var renderer = new Carto.RendererJS({ strict: true }); + + +describe('Field:filtered propery', function () { + it('should be false when the property is not filtered', function () { + var style = [ + '#layer {', + ' marker-fill: red;', + '}' + ].join('\n'); + var layers = renderer.render(style).layers[0].shader; + assert(!layers['marker-fill'].filtered); + }); + + it('should be true when the property is filtered', function () { + var style = [ + '#layer {', + ' [foo > 30] {', + ' marker-fill: red;', + ' }', + '}' + ].join('\n'); + + var layers = renderer.render(style).layers[0].shader; + assert(layers['marker-fill'].filtered); + }); + + it('should be true when the property is filtered at first level', function () { + var style = [ + '#layer [foo > 30] {', + ' marker-fill: red;', + '}`' + ].join('\n'); + + var layers = renderer.render(style).layers[0].shader; + assert(layers['marker-fill'].filtered); + }); + + it('should be false when the property is not filterd but there is another filtered properties', function () { + var style = [ + '#layer {', + ' marker-fill: red;', + ' [bar < 200]{', + ' marker-allow-overlap: false;', + ' }', + '}`' + ].join('\n'); + + var layers = renderer.render(style).layers[0].shader; + + assert(!layers['marker-fill'].filtered); + assert(layers['marker-allow-overlap'].filtered); + }); + + it('should be true when the property is filtered and have a default value', function () { + var style = [ + '#layer {', + ' marker-fill: red;', + ' [bar < 200]{', + ' marker-fill: blue;', + ' }', + '}`' + ].join('\n'); + var layers = renderer.render(style).layers[0].shader; + + assert(layers['marker-fill'].filtered); + }); +}); \ No newline at end of file