Further performance tuning, about a 10% improvement on
openstreetmap-carto
This commit is contained in:
parent
baab7dd0ec
commit
43073fa1e8
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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'));
|
||||
|
@ -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) {
|
||||
|
@ -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'));
|
||||
|
@ -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"
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user