Adding make doc command, making renderer docs better for docco, writing some docs.

This commit is contained in:
Tom MacWright 2011-02-02 16:24:20 -05:00
parent 10088c473f
commit 490b51c6fd
3 changed files with 113 additions and 99 deletions

View File

@ -10,4 +10,7 @@ test:
bin/expresso -I lib test/${only}.test.js
endif
doc:
docco lib/mess/*.js
.PHONY: test

View File

@ -311,10 +311,8 @@ mess.Parser = function Parser(env) {
}
}
/**
* Get an array of Ruleset objects, flattened
* and sorted according to specificitySort
*/
// Get an array of Ruleset objects, flattened
// and sorted according to specificitySort
root.toList = (function() {
var line, lines, column, _ = require('underscore')._;
return function(env) {
@ -336,15 +334,13 @@ mess.Parser = function Parser(env) {
};
})();
/**
* Sort rules by specificity: this function expects selectors to be
* split already.
*
* Written to be used as a .sort(Function);
* argument.
*
* [1, 0, 0, 467] > [0, 0, 1, 520]
*/
// Sort rules by specificity: this function expects selectors to be
// split already.
//
// Written to be used as a .sort(Function);
// argument.
//
// [1, 0, 0, 467] > [0, 0, 1, 520]
var specificitySort = function(a, b) {
var as = a.selector.specificity();
var bs = b.selector.specificity();

View File

@ -9,12 +9,8 @@ var path = require('path'),
require.paths.unshift(path.join(__dirname, '..', 'lib'));
/**
* Rendering circuitry for JSON map manifests.
*
* This is node-only for the time being.
*/
// Rendering circuitry for JSON map manifests.
// This is node-only for the time being.
mess.Renderer = function Renderer(env) {
env = _.extend({}, env);
if (!env.debug) env.debug = false;
@ -23,19 +19,15 @@ mess.Renderer = function Renderer(env) {
if (!env.validation_data) env.validation_data = false;
return {
/**
* Keep a copy of passed-in environment variables
*/
// Keep a copy of passed-in environment variables
env: env,
/**
* Ensure that map layers have a populated SRS value and attempt to
* autodetect SRS if missing. Requires that node-srs is available and
* that any remote datasources have been localized.
*
* @param {Object} m map object.
* @param {Function} callback
*/
// Ensure that map layers have a populated SRS value and attempt to
// autodetect SRS if missing. Requires that node-srs is available and
// that any remote datasources have been localized.
//
// - @param {Object} m map object.
// - @param {Function} callback
ensureSRS: function(m, callback) {
Step(
function autodetectSRS() {
@ -48,6 +40,8 @@ mess.Renderer = function Renderer(env) {
fs.readdir(path.dirname(l.Datasource.file), this);
},
function(err, files) {
// Confirm that layers have .prj files
// in line with the .shp and .dbf, etc files.
var prj = _.detect(files, function(f) {
return path.extname(f).toLowerCase() == '.prj';
});
@ -78,15 +72,13 @@ mess.Renderer = function Renderer(env) {
);
},
/**
* Download any file-based remote datsources.
*
* Usable as an entry point: does not expect any modification to
* the map object beyond JSON parsing.
*
* @param {Object} m map object.
* @param {Function} callback
*/
// Download any file-based remote datsources.
//
// Usable as an entry point: does not expect any modification to
// the map object beyond JSON parsing.
//
// - @param {Object} m map object.
// - @param {Function} callback
localizeExternals: function(m, callback) {
var that = this;
if (env.only_validate === true) {
@ -130,12 +122,10 @@ mess.Renderer = function Renderer(env) {
);
},
/**
* Download any remote stylesheets
*
* @param {Object} m map object.
* @param {Function} callback
*/
// Download any remote stylesheets
//
// - @param {Object} m map object.
// - @param {Function} callback
localizeStyle: function(m, callback) {
var that = this;
Step(
@ -144,6 +134,8 @@ mess.Renderer = function Renderer(env) {
m.Stylesheet.forEach(function(s, k) {
if (!s.id) {
var next = group();
// TODO: handle stylesheet externals
// that fail.
new External(that.env, s)
.on('complete', function(external) {
m.Stylesheet[k] = external.path();
@ -159,16 +151,14 @@ mess.Renderer = function Renderer(env) {
);
},
/**
* Compile (already downloaded) styles with mess.js,
* calling callback with an array of [map object, [stylesheet objects]]
*
* Called with the results of localizeStyle or localizeExternals:
* expects not to handle downloading.
*
* @param {Object} m map object.
* @param {Function} callback
*/
// Compile (already downloaded) styles with mess.js,
// calling callback with an array of [map object, [stylesheet objects]]
//
// Called with the results of localizeStyle or localizeExternals:
// expects not to handle downloading.
//
// - @param {Object} m map object.
// - @param {Function} callback
style: function(m, callback) {
var that = this;
Step(
@ -176,8 +166,13 @@ mess.Renderer = function Renderer(env) {
var group = this.group();
m.Stylesheet.forEach(function(s) {
var next = group();
// If a stylesheet is an object with an id
// property, it will have a .data property
// as well which can be parsed directly
if (s.id) {
next(null, [s.id, s.data]);
// Otherwise the value is assumed to be a
// string and is read from the filesystem
} else {
fs.readFile(s, 'utf-8', function(err, data) {
next(err, [s, data]);
@ -191,6 +186,9 @@ mess.Renderer = function Renderer(env) {
results.forEach(function(result) {
var next = group();
var parsingTime = +new Date;
// Maintain a parsing environment from
// stylesheet to stylesheet, so that frames
// and effects are maintained.
var parse_env = _.extend(
_.extend(
that.env,
@ -219,17 +217,18 @@ mess.Renderer = function Renderer(env) {
);
},
/**
* Split definitions into sub-lists of definitions
* containing rules pertaining to only one
* symbolizer each
*/
// Split definitions into sub-lists of definitions
// containing rules pertaining to only one
// symbolizer each
splitSymbolizers: function(definitions) {
var splitTime = +new Date;
var bySymbolizer = {};
for (var i = 0; i < definitions.length; i++) {
definitions[i].symbolizers().forEach(function(sym) {
var index = sym + (definitions[i].selector.attachment ? '/' + definitions[i].selector.attachment : '');
// Maintain attachments
var index = sym + (definitions[i].selector.attachment ?
'/' + definitions[i].selector.attachment :
'');
if(!bySymbolizer[index]) {
bySymbolizer[index] = [];
}
@ -237,15 +236,14 @@ mess.Renderer = function Renderer(env) {
definitions[i].filterSymbolizer(sym));
});
}
if (env.debug) console.warn('Splitting symbolizers: ' + ((new Date - splitTime)) + 'ms');
if (env.debug) console.warn('Splitting symbolizers: '
+ ((new Date - splitTime)) + 'ms');
return bySymbolizer;
},
/**
* Pick the 'winners' - all elements that select
* properly. Apply inherited styles from their
* ancestors to them.
*/
// Pick the 'winners' - all elements that select
// properly. Apply inherited styles from their
// ancestors to them.
processChain: function(definitions) {
var processTime = +new Date();
// definitions are ordered in specificity,
@ -261,8 +259,8 @@ mess.Renderer = function Renderer(env) {
}
}
if (env.debug) console.warn('Processing time: ' + ((new Date - processTime)) + 'ms');
if (env.debug) console.warn('Processing time: '
+ ((new Date - processTime)) + 'ms');
return definitions;
},
@ -294,9 +292,14 @@ mess.Renderer = function Renderer(env) {
var filters;
for (var id1 in byFilter) {
for (var id2 in byFilter) {
// If selectors are identical, they can't be merged.
if (id1 === id2) continue;
// If selectors don't cover the exact same zoom range,
// they can't be merged.
if (byFilter[id1].zoom != byFilter[id2].zoom) continue;
if (filters = tree.Filter.mergable(byFilter[id1].filters, byFilter[id2].filters)) {
if (filters = tree.Filter.mergable(
byFilter[id1].filters,
byFilter[id2].filters)) {
byFilter[id1].filters = filters;
delete byFilter[id2];
}
@ -310,6 +313,10 @@ mess.Renderer = function Renderer(env) {
return result;
},
// Resolve conditions: with the current state of Mapnik
// and the goal of CSS-like behavior, it is necessary to
// ensure that only one Rule, and thus only one Filter,
// is applied out of a Style.
resolveConditions: function(definitions) {
var renderer = this;
var resolvingTime = +new Date;
@ -364,20 +371,18 @@ mess.Renderer = function Renderer(env) {
input = next;
}
if (env.debug) console.warn('Resolving time: ' + ((new Date - resolvingTime)) + 'ms');
// process.exit();
if (env.debug) console.warn('Resolving time: '
+ ((new Date - resolvingTime)) + 'ms');
return rules;
},
/**
* Find a rule like Map { background-color: #fff; },
* if any, and return a list of properties to be inserted
* into the <Map element of the resulting XML.
*
* @param {Array} rulesets the output of toList.
* @param {Object} env.
* @return {String} rendered properties.
*/
// Find a rule like Map { background-color: #fff; },
// if any, and return a list of properties to be inserted
// into the <Map element of the resulting XML.
//
// - @param {Array} rulesets the output of toList.
// - @param {Object} env.
// - @return {String} rendered properties.
getMapProperties: function(rulesets, env) {
var properties = [];
rulesets.filter(function(r) {
@ -391,13 +396,11 @@ mess.Renderer = function Renderer(env) {
return properties.join('');
},
/**
* Prepare full XML map output. Called with the results
* of this.style
*
* @param {Array} res array of [map object, stylesheets].
* @param {Function} callback
*/
// Prepare full XML map output. Called with the results
// of this.style
//
// - @param {Array} res array of [map object, stylesheets].
// - @param {Function} callback
template: function(err, m, stylesheets, callback) {
// frames is a container for variables and other less.js
// constructs.
@ -425,8 +428,12 @@ mess.Renderer = function Renderer(env) {
return rulesets[1].toList(env);
}));
// Iterate through layers and create styles custom-built
// for each of them, and apply those styles to the layers.
m.Layer.forEach(function(l) {
l.styles = [];
// Classes are given as space-separated alphanumeric
// strings.
var classes = (l['class'] || '').split(/\s+/g);
var matching = rulesets.filter(function(ruleset) {
@ -464,7 +471,11 @@ mess.Renderer = function Renderer(env) {
function() {
var group = this.group();
var map_properties = that.getMapProperties(rulesets, env);
if (env.only_validate !== true && env.deferred_externals.length) {
// The only_validate flag can be set to prevent
// any externals from being downloaded - for instance,
// when only validation is needed.
if (env.only_validate !== true &&
env.deferred_externals.length) {
env.deferred_externals.forEach(function(def) {
var next = group();
new External(that.env, def)
@ -501,18 +512,20 @@ mess.Renderer = function Renderer(env) {
}
});
callback(env.errors.length ? env.errors : null, output.join('\n'));
callback(env.errors.length ?
env.errors :
null,
output.join('\n'));
}
);
},
/**
* Prepare a JML document (given as a string) into a
* fully-localized XML file ready for Mapnik2 consumption
*
* @param {String} str the JSON file as a string.
* @param {Function} callback to be called with err, XML representation.
*/
// Prepare a MML document (given as a string) into a
// fully-localized XML file ready for Mapnik2 consumption
//
// - @param {String} str the JSON file as a string.
// - @param {Function} callback to be called with err,
// XML representation.
render: function(str, callback) {
var m = (typeof str == "string") ? JSON.parse(str) : str,
that = this;
@ -521,11 +534,13 @@ mess.Renderer = function Renderer(env) {
this.localizeExternals(m, function(err, res) {
that.ensureSRS(res, function(err, res) {
that.localizeStyle(res, function(err, res) {
if (env.debug) console.warn('Localizing time: ' + ((new Date - localizingTime)) + 'ms');
if (env.debug) console.warn('Localizing time: '
+ ((new Date - localizingTime)) + 'ms');
that.style(res, function(err, m, res) {
var compilingTime = +new Date;
that.template(err, m, res, function(err, res) {
if (env.debug) console.warn('COMPILING TIME: ' + ((new Date - compilingTime)) + 'ms');
if (env.debug) console.warn('COMPILING TIME: '
+ ((new Date - compilingTime)) + 'ms');
callback(err, res);
});
});