Rewrite zoom filters as objects, support variables in zoom definitions.

This commit is contained in:
Tom MacWright 2012-12-20 12:37:38 -05:00
parent 3c4baaf8cb
commit 04cf9013a8
8 changed files with 65 additions and 35 deletions

View File

@ -598,7 +598,7 @@ carto.Parser = function Parser(env) {
var a, attachment;
var e, elements = [];
var f, filters = new tree.Filterset();
var z, zoom = tree.Zoom.all;
var z, zooms = [];
var segments = 0, conditions = 0;
while (
@ -611,7 +611,7 @@ carto.Parser = function Parser(env) {
if (e) {
elements.push(e);
} else if (z) {
zoom &= z;
zooms.push(z);
conditions++;
} else if (f) {
filters.add(f);
@ -630,7 +630,7 @@ carto.Parser = function Parser(env) {
}
if (segments) {
return new tree.Selector(filters, zoom, elements, attachment, conditions, memo);
return new tree.Selector(filters, zooms, elements, attachment, conditions, memo);
}
},
@ -653,9 +653,8 @@ carto.Parser = function Parser(env) {
var op, val;
if ($(/^\[zoom/g) &&
(op = $(this.entities.comparison)) &&
(val = $(/^\d+/)) &&
$(']')) {
return tree.Zoom(op, val, memo);
(val = $(this.entities.variable) || $(this.entities.dimension)) && $(']')) {
return new tree.Zoom(op, val, memo);
}
},

View File

@ -81,7 +81,7 @@ function symbolizerList(sym_order) {
}
tree.Definition.prototype.symbolizersToXML = function(env, symbolizers, zoom) {
var xml = ' <Rule>\n' + tree.Zoom.toXML(zoom).join('') +
var xml = ' <Rule>\n' + zoom.toXML(env).join('') +
this.filters.toXML(env);
var key;
@ -183,8 +183,8 @@ tree.Definition.prototype.toXML = function(env, existing) {
var filter = this.filters.toString();
if (!(filter in existing)) existing[filter] = tree.Zoom.all;
var available = tree.Zoom.all, xml = '', zoom, symbolizers;
var zooms = { available: tree.Zoom.all };
var available = tree.Zoom.all, xml = '', zoom, symbolizers,
zooms = { available: tree.Zoom.all };
for (var i = 0; i < this.rules.length && available; i++) {
zooms.rule = this.rules[i].zoom;
if (!(existing[filter] & zooms.rule)) continue;
@ -192,7 +192,7 @@ tree.Definition.prototype.toXML = function(env, existing) {
while (zooms.current = zooms.rule & available) {
if (symbolizers = this.collectSymbolizers(zooms, i)) {
if (!(existing[filter] & zooms.current)) continue;
xml += this.symbolizersToXML(env, symbolizers, existing[filter] & zooms.current);
xml += this.symbolizersToXML(env, symbolizers, (new tree.Zoom()).setZoom(existing[filter] & zooms.current));
existing[filter] &= ~zooms.current;
}
}

View File

@ -10,7 +10,6 @@ tree.Filter = function Filter(key, op, val, index, filename) {
this.id = this.key + this.op + this.val;
};
// xmlsafe, numeric, suffix
var ops = {
'<': [' &lt; ', 'numeric'],

View File

@ -19,8 +19,7 @@ tree.Filterset.prototype.toXML = function(env) {
tree.Filterset.prototype.toString = function() {
var arr = [];
for (var id in this.filters) arr.push(this.filters[id].id);
arr.sort();
return arr.join('\t');
return arr.sort().join('\t');
};
tree.Filterset.prototype.eval = function(env) {
@ -40,7 +39,7 @@ tree.Filterset.prototype.clone = function() {
// Note: other has to be a tree.Filterset.
tree.Filterset.prototype.cloneWith = function(other) {
var additions;
var additions = [];
for (var id in other.filters) {
var status = this.addable(other.filters[id]);
// status is true, false or null. if it's null we don't fail this
@ -50,14 +49,13 @@ tree.Filterset.prototype.cloneWith = function(other) {
}
if (status === true) {
// Adding the filter will override another value.
if (!additions) additions = [];
additions.push(other.filters[id]);
}
}
// Adding the other filters doesn't make this filterset invalid, but it
// doesn't add anything to it either.
if (!additions) {
if (!additions.length) {
return null;
}
@ -88,7 +86,14 @@ tree.Filterset.prototype.addable = function(filter) {
switch (filter.op) {
case '=':
if (key + '=' in this.filters) return (this.filters[key + '='].val != value) ? false : null;
// if there is already foo= and we're adding foo=
if (key + '=' in this.filters) {
if (this.filters[key + '='].val != value) {
return false;
} else {
return null;
}
}
if (key + '!=' + value in this.filters) return false;
if (key + '>' in this.filters && this.filters[key + '>'].val >= value) return false;
if (key + '<' in this.filters && this.filters[key + '<'].val <= value) return false;

View File

@ -77,11 +77,23 @@ tree.Ruleset.prototype = {
});
return this._lookups[key] = rules;
},
// Zooms can use variables. This replaces tree.Zoom objects on selectors
// with simple bit-arrays that we can compare easily.
evalZooms: function(env) {
for (var i = 0; i < this.selectors.length; i++) {
var zval = tree.Zoom.all;
for (var z = 0; z < this.selectors[i].zoom.length; z++) {
zval = zval & this.selectors[i].zoom[z].eval(env).zoom;
}
this.selectors[i].zoom = zval;
}
},
flatten: function(result, parents, env) {
var selectors = [], i, j;
if (this.selectors.length === 0) {
env.frames = env.frames.concat(this.rules);
}
this.evalZooms(env);
for (i = 0; i < this.selectors.length; i++) {
var child = this.selectors[i];

View File

@ -14,14 +14,13 @@ tree.Variable.prototype = {
eval: function(env) {
var variable,
v,
that = this,
name = this.name;
if (this._css) return this._css;
var thisframe = env.frames.filter(function(f) {
return f.name == that.name;
});
return f.name == this.name;
}.bind(this));
if (thisframe.length) {
return thisframe[0].value.eval(env);
} else {

View File

@ -4,7 +4,22 @@ 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, 10);
this.op = op;
this.value = value;
this.index = index;
};
tree.Zoom.prototype.setZoom = function(zoom) {
this.zoom = zoom;
return this;
};
tree.Zoom.prototype.eval = function(env) {
var start = 0,
end = Infinity,
value = parseInt(this.value.eval(env).toString(), 10),
zoom = 0;
if (value > tree.Zoom.maxZoom || value < 0) {
throw {
message: 'Only zoom levels between 0 and ' +
@ -13,13 +28,10 @@ tree.Zoom = function(op, value, index) {
};
}
var start = 0,
end = Infinity,
zoom = 0;
switch (op) {
switch (this.op) {
case '=':
return 1 << value;
this.zoom = 1 << value;
return this;
case '>':
start = value + 1;
break;
@ -38,7 +50,12 @@ tree.Zoom = function(op, value, index) {
zoom |= (1 << i);
}
}
return zoom;
this.zoom = zoom;
return this;
};
tree.Zoom.prototype.toString = function() {
return this.zoom;
};
// Covers all zoomlevels from 0 to 22
@ -74,12 +91,12 @@ tree.Zoom.ranges = {
};
// Only works for single range zooms. `[XXX....XXXXX.........]` is invalid.
tree.Zoom.toXML = function(zoom) {
tree.Zoom.prototype.toXML = function() {
var conditions = [];
if (zoom != tree.Zoom.all) {
if (this.zoom != tree.Zoom.all) {
var start = null, end = null;
for (var i = 0; i <= tree.Zoom.maxZoom; i++) {
if (zoom & (1 << i)) {
if (this.zoom & (1 << i)) {
if (start === null) start = i;
end = i;
}
@ -92,11 +109,10 @@ tree.Zoom.toXML = function(zoom) {
return conditions;
};
tree.Zoom.toString = function(zoom) {
tree.Zoom.prototype.toString = function() {
var str = '';
for (var i = 0; i <= tree.Zoom.maxZoom; i++) {
str += (zoom & (1 << i)) ? 'X' : '.';
str += (this.zoom & (1 << i)) ? 'X' : '.';
}
return str;
};

View File

@ -1,5 +1,5 @@
#world {
[zoom>9] { marker-height: 4; }
[zoom=9] { marker-height: 4; }
[zoom>6][zoom<10] { marker-height: 3; }
[zoom>3][zoom<7] { marker-height: 2; }
[zoom<4] { marker-height: 1; }