Add regex filter, tests, clean up code more. Fixes #66

This commit is contained in:
Tom MacWright 2012-05-08 15:26:08 -04:00
parent 9960cd2667
commit 262ef7e722
11 changed files with 115 additions and 49 deletions

View File

@ -438,16 +438,15 @@ carto.Parser = function Parser(env) {
// "milky way" 'he\'s the one!'
//
quoted: function() {
var str;
if (input.charAt(i) !== '"' && input.charAt(i) !== "'") return;
if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) {
var str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/);
if (str) {
return new tree.Quoted(str[0], str[1] || str[2]);
}
},
comparison: function() {
var str = $(/^=|!=|<=|>=|<|>/);
var str = $(/^=~|=|!=|<=|>=|<|>/);
if (str) {
return str;
}

View File

@ -25,9 +25,15 @@ tree.Directive.prototype = {
env.frames.shift();
return this;
},
variable: function(name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) },
find: function() { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) },
rulesets: function() { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }
variable: function(name) {
return tree.Ruleset.prototype.variable.call(this.ruleset, name);
},
find: function() {
return tree.Ruleset.prototype.find.apply(this.ruleset, arguments);
},
rulesets: function() {
return tree.Ruleset.prototype.rulesets.apply(this.ruleset);
}
};
})(require('../tree'));

View File

@ -1,6 +1,8 @@
(function(tree) {
tree.Expression = function Expression(value) { this.value = value };
tree.Expression = function Expression(value) {
this.value = value;
};
tree.Expression.prototype = {
eval: function(env) {
if (this.value.length > 1) {

View File

@ -19,7 +19,7 @@ tree.Filter = function Filter(key, op, val, index, filename) {
this.val = val;
}
if (this.op !== '=' && this.op !== '!=') {
if (ops[this.op][1]) {
this.val = 1 * this.val;
}
@ -27,18 +27,19 @@ tree.Filter = function Filter(key, op, val, index, filename) {
};
// XML-safe versions of comparators
var opXML = {
'<': '&lt;',
'>': '&gt;',
'=': '=',
'!=': '!=',
'<=': '&lt;=',
'>=': '&gt;='
// xmlsafe, numeric, suffix
var ops = {
'<': [' &lt; ', true],
'>': [' &gt; ', true],
'=': [' = ', false],
'!=': [' != ', false],
'<=': [' &lt;= ', true],
'>=': [' &gt;= ', true],
'=~': ['.match(', false, ')']
};
tree.Filter.prototype.toXML = function(env) {
if (this.op !== '=' && this.op !== '!=' && isNaN(this.val)) {
if (ops[this.op][1] && isNaN(this.val)) {
env.error({
message: 'Cannot use operator "' + this.op + '" with value ' + this.val,
index: this.index,
@ -50,7 +51,7 @@ tree.Filter.prototype.toXML = function(env) {
if (this._key) var key = this._key.toString(false);
if (this._val) var val = this._val.toString(this._val.is == 'string');
return '[' + (key || this.key) + '] ' + opXML[this.op] + ' ' + (val || this.val);
return '[' + (key || this.key) + ']' + ops[this.op][0] + '' + (val || this.val) + (ops[this.op][2] || '');
};
tree.Filter.prototype.toString = function() {

View File

@ -66,12 +66,14 @@ Object.defineProperty(tree.Filterset.prototype, 'cloneWith', {
// We can add the rules that are already present without going through the
// add function as a Filterset is always in it's simplest canonical form.
for (var id in this)
for (var id in this) {
clone[id] = this[id];
}
// Only add new filters that actually change the filter.
while (id = additions.shift())
while (id = additions.shift()) {
clone.add(id);
}
return clone;
}
@ -98,12 +100,10 @@ Object.defineProperty(tree.Filterset.prototype, 'addable', {
case '!=':
if (key + '=' in this) return (this[key + '='].val == value) ? false : null;
if (key + '!=' + value in this) return null;
if (key + '>' in this && this[key + '>'].val >= value) return null;
if (key + '<' in this && this[key + '<'].val <= value) return null;
if (key + '>=' in this && this[key + '>='].val > value) return null;
if (key + '<=' in this && this[key + '<='].val < value) return null;
return true;
case '>':
@ -151,9 +151,11 @@ Object.defineProperty(tree.Filterset.prototype, 'add', {
switch (filter.op) {
case '=':
for (var id in this)
if (this[id].key == key)
for (var id in this) {
if (this[id].key == key) {
delete this[id];
}
}
this[key + '='] = filter;
break;
@ -161,17 +163,28 @@ Object.defineProperty(tree.Filterset.prototype, 'add', {
this[key + '!=' + filter.val] = filter;
break;
case '=~':
this[key + '=~' + filter.val] = filter;
break;
case '>':
for (var id in this)
if (this[id].key == key && this[id].val <= filter.val)
// If there are other filters that are also >
// but are less than this one, they don't matter, so
// remove them.
for (var id in this) {
if (this[id].key == key && this[id].val <= filter.val) {
delete this[id];
}
}
this[key + '>'] = filter;
break;
case '>=':
for (var id in this)
if (this[id].key == key && this[id].val < filter.val)
for (var id in this) {
if (this[id].key == key && this[id].val < filter.val) {
delete this[id];
}
}
if (key + '!=' + filter.val in this) {
delete this[key + '!=' + filter.val];
filter.op = '>';
@ -183,16 +196,20 @@ Object.defineProperty(tree.Filterset.prototype, 'add', {
break;
case '<':
for (var id in this)
if (this[id].key == key && this[id].val >= filter.val)
for (var id in this) {
if (this[id].key == key && this[id].val >= filter.val) {
delete this[id];
}
}
this[key + '<'] = filter;
break;
case '<=':
for (var id in this)
if (this[id].key == key && this[id].val > filter.val)
for (var id in this) {
if (this[id].key == key && this[id].val > filter.val) {
delete this[id];
}
}
if (key + '!=' + filter.val in this) {
delete this[key + '!=' + filter.val];
filter.op = '<';

View File

@ -26,13 +26,13 @@ tree.FontSet = function FontSet(env, fonts) {
};
tree.FontSet.prototype.toXML = function(env) {
return '<FontSet name="'
+ this.name
+ '">\n'
+ this.fonts.map(function(f) {
return '<FontSet name="' +
this.name +
'">\n' +
this.fonts.map(function(f) {
return ' <Font face-name="' + f +'"/>';
}).join('\n')
+ '\n</FontSet>'
}).join('\n') +
'\n</FontSet>';
};
})(require('../tree'));

View File

@ -10,8 +10,8 @@ tree.Keyword = function Keyword(value) {
this.is = special[value] ? special[value] : 'keyword';
};
tree.Keyword.prototype = {
eval: function() { return this },
toString: function() { return this.value }
eval: function() { return this; },
toString: function() { return this.value; }
};
})(require('../tree'));

View File

@ -4,11 +4,11 @@ var tree = require('../tree');
// and stores them as bit-sequences so that they can be combined,
// inverted, and compared quickly.
tree.Zoom = function(op, value, index) {
value = parseInt(value);
value = parseInt(value, 10);
if (value > tree.Zoom.maxZoom || value < 0) {
throw {
message: 'Only zoom levels between 0 and '
+ tree.Zoom.maxZoom + ' supported.',
message: 'Only zoom levels between 0 and ' +
tree.Zoom.maxZoom + ' supported.',
index: index
};
}
@ -81,14 +81,14 @@ tree.Zoom.toXML = function(zoom) {
var start = null, end = null;
for (var i = 0; i <= tree.Zoom.maxZoom; i++) {
if (zoom & (1 << i)) {
if (start == null) start = i;
if (start === null) start = i;
end = i;
}
}
if (start > 0) conditions.push(' <MaxScaleDenominator>'
+ tree.Zoom.ranges[start] + '</MaxScaleDenominator>\n');
if (end < 22) conditions.push(' <MinScaleDenominator>'
+ tree.Zoom.ranges[end + 1] + '</MinScaleDenominator>\n');
if (start > 0) conditions.push(' <MaxScaleDenominator>' +
tree.Zoom.ranges[start] + '</MaxScaleDenominator>\n');
if (end < 22) conditions.push(' <MinScaleDenominator>' +
tree.Zoom.ranges[end + 1] + '</MinScaleDenominator>\n');
}
return conditions;
};

14
test/rendering/regex.mml Normal file
View File

@ -0,0 +1,14 @@
{
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Stylesheet": [
"regex.mss"
],
"Layer": [{
"name": "world",
"srs": "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over",
"Datasource": {
"file": "http://tilemill-data.s3.amazonaws.com/test_data/shape_demo.zip",
"type": "shape"
}
}]
}

5
test/rendering/regex.mss Normal file
View File

@ -0,0 +1,5 @@
#world[ISO =~ "U*"] {
polygon-fill: #FFF;
line-color:#F00;
line-width: 0.5;
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Map[]>
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over" maximum-extent="-20037508.34,-20037508.34,20037508.34,20037508.34">
<Style name="world" filter-mode="first">
<Rule>
<Filter>([ISO].match('U*'))</Filter>
<PolygonSymbolizer fill="#ffffff" />
<LineSymbolizer stroke="#ff0000" stroke-width="0.5" />
</Rule>
</Style>
<Layer name="world"
srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over">
<StyleName>world</StyleName>
<Datasource>
<Parameter name="file"><![CDATA[[absolute path]]]></Parameter>
<Parameter name="type"><![CDATA[shape]]></Parameter>
</Datasource>
</Layer>
</Map>