Further performance tuning, about a 10% improvement on

openstreetmap-carto
This commit is contained in:
Tom MacWright 2012-12-26 22:23:04 -05:00
parent baab7dd0ec
commit 43073fa1e8
8 changed files with 90 additions and 119 deletions

View File

@ -1,14 +1,6 @@
var carto, tree, _;
if (typeof(process) !== 'undefined') {
carto = exports;
tree = require('./tree');
var carto = exports,
tree = require('./tree'),
_ = require('underscore');
} else {
if (typeof(window.carto) === 'undefined') { window.carto = {}; }
carto = window.carto;
tree = window.carto.tree = {};
}
// Token matching is done with the `$` function, which either takes
// a terminal string or regexp, or a non-terminal function to call.
@ -106,11 +98,7 @@ carto.Parser = function Parser(env) {
if (typeof(tok) === 'string') {
return input.charAt(i) === tok;
} else {
if (tok.test(chunks[j])) {
return true;
} else {
return false;
}
return !!tok.test(chunks[j]);
}
}
@ -726,9 +714,7 @@ carto.Parser = function Parser(env) {
},
// Expressions either represent mathematical operations,
// or white-space delimited Entities.
//
// @var * 2
// or white-space delimited Entities. @var * 2
expression: function() {
var e, delim, entities = [], d;
@ -747,4 +733,3 @@ carto.Parser = function Parser(env) {
};
return parser;
};

View File

@ -52,7 +52,7 @@ carto.Renderer.prototype.renderMSS = function render(data, callback) {
var bench_name = '\tStyle "'+style_name+'" (#'+k+') toXML';
if (env.benchmark) console.time(bench_name);
// env.effects can be modified by this call
output.push(carto.tree.Style.toXML(style_name, rule.attachment, rule, env));
output.push(carto.tree.StyleXML(style_name, rule.attachment, rule, env));
if (env.benchmark) console.timeEnd(bench_name);
}
if (env.benchmark) console.timeEnd('Total Style generation');
@ -126,7 +126,7 @@ carto.Renderer.prototype.render = function render(m, callback) {
styles.push(style_name);
// env.effects can be modified by this call
output.push(carto.tree.Style.toXML(style_name, rule.attachment, rule, env));
output.push(carto.tree.StyleXML(style_name, rule.attachment, rule, env));
}
if (styles.length) {
output.push(carto.tree.Layer.toXML(l, styles));

View File

@ -1,4 +1,5 @@
(function(tree) {
var _ = require('underscore');
tree.Call = function Call(name, args, index) {
this.name = name;
this.args = args;
@ -7,18 +8,14 @@ tree.Call = function Call(name, args, index) {
tree.Call.prototype = {
is: 'call',
//
// When evaluating a function call,
// we either find the function in `tree.functions` [1],
// in which case we call it, passing the evaluated arguments,
// or we simply print it out as it appeared originally [2].
//
// The *functions.js* file contains the built-in functions.
//
// The reason why we evaluate the arguments, is in the case where
// we try to pass a variable to a function, like: `saturate(@color)`.
// The function should receive the value, not the variable.
//
'eval': function(env) {
var args = this.args.map(function(a) { return a.eval(env); });
@ -48,9 +45,9 @@ tree.Call.prototype = {
};
}
} else {
var fn = tree.Reference.mapnikFunction(this.name);
if (!fn) {
var functions = tree.Reference.mapnikFunctions();
var fn = tree.Reference.mapnikFunctions[this.name];
if (fn === undefined) {
var functions = _.pairs(tree.Reference.mapnikFunctions);
// cheap closest, needs improvement.
var name = this.name;
var mean = functions.map(function(f) {
@ -70,10 +67,10 @@ tree.Call.prototype = {
value: 'undefined'
};
}
if (fn[1] !== args.length) {
if (fn !== args.length) {
env.error({
message: 'function ' + this.name + '() takes ' +
fn[1] + ' arguments and was given ' + args.length,
fn + ' arguments and was given ' + args.length,
index: this.index,
type: 'runtime',
filename: this.filename

View File

@ -61,9 +61,10 @@ tree.Definition.prototype.addRules = function(rules) {
// all elements it contains match.
tree.Definition.prototype.appliesTo = function(id, classes) {
for (var i = 0, l = this.elements.length; i < l; i++) {
if (!this.elements[i].matches(id, classes)) {
return false;
}
var elem = this.elements[i];
if (!(elem.wildcard ||
(elem.type === 'class' && classes[elem.clean]) ||
(elem.type === 'id' && id === elem.clean))) return false;
}
return true;
};

View File

@ -3,31 +3,28 @@
// An element is an id or class selector
tree.Element = function Element(value) {
this.value = value.trim();
if (this.value[0] === '#') {
this.type = 'id';
this.clean = this.value.replace(/^#/, '');
}
if (this.value[0] === '.') {
this.type = 'class';
this.clean = this.value.replace(/^\./, '');
}
if (this.value.indexOf('*') !== -1) {
this.type = 'wildcard';
}
};
// Determine the 'specificity matrix' of this
// specific selector
tree.Element.prototype.specificity = function() {
return [
(this.value[0] == '#') ? 1 : 0, // a
(this.value[0] == '.') ? 1 : 0 // b
(this.type === 'id') ? 1 : 0, // a
(this.type === 'class') ? 1 : 0 // b
];
};
tree.Element.prototype.toString = function() {
return this.value;
};
// Determine whether this element matches an id or classes.
// An element is a single id or class, or check whether the given
// array of classes contains this, or the id is equal to this.
//
// Takes a plain string for id and plain strings in the array of
// classes.
tree.Element.prototype.matches = function(id, classes) {
return (classes[this.value.replace(/^\./, '')]) ||
(this.value.replace(/^#/, '') === id) ||
(this.value === '*');
};
tree.Element.prototype.toString = function() { return this.value; };
})(require('../tree'));

View File

@ -4,39 +4,23 @@
// combinations.
(function(tree) {
var _ = require('underscore');
var mapnik_reference = require('mapnik-reference');
var _ = require('underscore'),
mapnik_reference = require('mapnik-reference');
tree.Reference = {
data: mapnik_reference.version.latest
};
tree.Reference = { data: mapnik_reference.version.latest };
/// @param version target mapnik version
/// @return true on success, false on error (unsupported mapnik version)
tree.Reference.setVersion = function(version) {
if (mapnik_reference.version.hasOwnProperty(version)) {
tree.Reference.data = mapnik_reference.version[version];
// caches
tree.Reference.selector_cache = generateSelectorCache(tree.Reference.data);
tree.Reference.mapnikFunctions = generateMapnikFunctions(tree.Reference.data);
return true;
} else {
return false;
}
};
tree.Reference.required_prop_list_cache = {};
tree.Reference.selector_cache = tree.Reference.selector_cache || (function() {
var index = {};
for (var i in tree.Reference.data.symbolizers) {
for (var j in tree.Reference.data.symbolizers[i]) {
if (tree.Reference.data.symbolizers[i][j].hasOwnProperty('css')) {
index[tree.Reference.data.symbolizers[i][j].css] =
[tree.Reference.data.symbolizers[i][j], i, j];
}
}
}
return index;
})();
tree.Reference.validSelector = function(selector) {
return !!tree.Reference.selector_cache[selector];
};
@ -59,43 +43,52 @@ tree.Reference.symbolizer = function(selector) {
}
};
// For transform properties and image-filters,
// mapnik has its own functions.
tree.Reference.mapnikFunctions = function() {
var functions = [];
for (var i in tree.Reference.data.symbolizers) {
for (var j in tree.Reference.data.symbolizers[i]) {
if (tree.Reference.data.symbolizers[i][j].type === 'functions') {
functions = functions.concat(tree.Reference.data.symbolizers[i][j].functions);
function generateSelectorCache(data) {
var index = {};
for (var i in data.symbolizers) {
for (var j in data.symbolizers[i]) {
if (data.symbolizers[i][j].hasOwnProperty('css')) {
index[data.symbolizers[i][j].css] = [data.symbolizers[i][j], i, j];
}
}
}
return index;
}
function generateMapnikFunctions(data) {
var functions = {};
for (var i in data.symbolizers) {
for (var j in data.symbolizers[i]) {
if (data.symbolizers[i][j].type === 'functions') {
for (var k = 0; k < data.symbolizers[i][j].functions.length; k++) {
var fn = data.symbolizers[i][j].functions[k];
functions[fn[0]] = fn[1];
}
}
}
}
return functions;
};
}
// For transform properties and image-filters,
// mapnik has its own functions.
tree.Reference.mapnikFunction = function(name) {
return _.find(this.mapnikFunctions(), function(f) {
return f[0] === name;
});
};
tree.Reference.requiredPropertyList = function(symbolizer_name) {
if (this.required_prop_list_cache[symbolizer_name]) {
return this.required_prop_list_cache[symbolizer_name];
}
var properties = [];
for (var j in tree.Reference.data.symbolizers[symbolizer_name]) {
if (tree.Reference.data.symbolizers[symbolizer_name][j].required) {
properties.push(tree.Reference.data.symbolizers[symbolizer_name][j].css);
function generateRequiredProperties(data) {
var cache = {};
for (var symbolizer_name in data.symbolizers) {
cache[symbolizer_name] = [];
for (var j in data.symbolizers[symbolizer_name]) {
if (data.symbolizers[symbolizer_name][j].required) {
cache[symbolizer_name].push(data.symbolizers[symbolizer_name][j].css);
}
}
}
return this.required_prop_list_cache[symbolizer_name] = properties;
};
return cache;
}
tree.Reference.selector_cache = generateSelectorCache(tree.Reference.data);
tree.Reference.mapnikFunctions = generateMapnikFunctions(tree.Reference.data);
tree.Reference.required_cache = generateRequiredProperties(tree.Reference.data);
tree.Reference.requiredProperties = function(symbolizer_name, rules) {
var req = tree.Reference.requiredPropertyList(symbolizer_name);
var req = tree.Reference.required_cache[symbolizer_name];
for (var i in req) {
if (!(req[i] in rules)) {
return 'Property ' + req[i] + ' required for defining ' +
@ -104,9 +97,7 @@ tree.Reference.requiredProperties = function(symbolizer_name, rules) {
}
};
/**
* TODO: finish implementation - this is dead code
*/
// TODO: finish implementation - this is dead code
tree.Reference._validateValue = {
'font': function(env, value) {
if (env.validation_data && env.validation_data.fonts) {

View File

@ -1,24 +1,24 @@
(function(tree) {
var _ = require('underscore');
tree.Style = function Style(name, attachment, definitions) {
};
tree.Style.toXML = function(name, attachment, definitions, env) {
tree.StyleXML = function(name, attachment, definitions, env) {
var existing = {};
var image_filters = [], comp_op = [], opacity = [];
function byName(name) {
return _.flatten(definitions.map(function(definition) {
return definition.rules.filter(function(rule) {
return (rule.name === name);
});
}));
for (var i = 0; i < definitions.length; i++) {
for (var j = 0; j < definitions[i].rules.length; j++) {
if (definitions[i].rules[j].name === 'image-filters') {
image_filters.push(definitions[i].rules[j]);
}
if (definitions[i].rules[j].name === 'comp-op') {
comp_op.push(definitions[i].rules[j]);
}
if (definitions[i].rules[j].name === 'opacity') {
opacity.push(definitions[i].rules[j]);
}
}
}
var image_filters = byName('image-filters');
var comp_op = byName('comp-op');
var opacity = byName('opacity');
var rules = definitions.map(function(definition) {
return definition.toXML(env, existing);
});
@ -43,4 +43,4 @@ tree.Style.toXML = function(name, attachment, definitions, env) {
return '<Style name="' + name + '" filter-mode="first" ' + attrs_xml + '>\n' + rule_string + '</Style>';
};
})(require('../tree'));
})(require('../tree'));

View File

@ -35,7 +35,7 @@
"node": ">=0.4.x"
},
"dependencies": {
"underscore": "~1.3.3",
"underscore": "~1.4.3",
"mapnik-reference": "~5.0.0",
"xml2js": "~0.1.13"
},