diff --git a/CHANGELOG.md b/CHANGELOG.md index 3854c57..6eb0416 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ ## Changelog +## 0.9.0 + +* Adds the modulus operator `%` as an option +* Adds a new field-type like `[FIELD]` instead of "[FIELD" + +### 0.8.0 + +* Supports function syntax for transforms, optionally with variables and arguments. + ### 0.7.0 * Support an `opacity` property on any style that is a style-level property diff --git a/bin/carto b/bin/carto index 5ab0274..1b0b9b8 100755 --- a/bin/carto +++ b/bin/carto @@ -2,7 +2,7 @@ var path = require('path'), fs = require('fs'), - sys = require('sys'), + util = require('util'), carto = require('carto'); var args = process.argv.slice(1); @@ -17,7 +17,7 @@ args = args.filter(function (arg) { switch (arg) { case 'v': case 'version': - sys.puts("carto " + carto.version.join('.') + " (Carto map stylesheet compiler)"); + util.puts("carto " + carto.version.join('.') + " (Carto map stylesheet compiler)"); process.exit(0); break; case 'b': @@ -46,7 +46,7 @@ if (input && input[0] != '/') { } if (!input) { - sys.puts("carto: no input files"); + util.puts("carto: no input files"); process.exit(1); } @@ -57,14 +57,14 @@ if (options.benchmark) { try { var data = fs.readFileSync(input, 'utf-8'); } catch(err) { - sys.puts("carto: " + err.message.replace(/^[A-Z]+, /, '')); + util.puts("carto: " + err.message.replace(/^[A-Z]+, /, '')); process.exit(1); } try { data = JSON.parse(data); } catch(err) { - sys.puts("carto: " + err.message.replace(/^[A-Z]+, /, '')); + util.puts("carto: " + err.message.replace(/^[A-Z]+, /, '')); process.exit(1); } @@ -98,7 +98,7 @@ function compile(err, data) { process.exit(1); } else { if (!options.benchmark) { - sys.puts(output); + util.puts(output); } else { var duration = (+new Date) - start; console.log('TOTAL: ' + (duration) + 'ms'); @@ -107,9 +107,9 @@ function compile(err, data) { }); } catch (e) { if (e.stack) { - sys.error(e.stack); + util.error(e.stack); } else { - sys.error(e); + util.error(e); } } }; diff --git a/lib/carto/functions.js b/lib/carto/functions.js index 47129ef..8f45a44 100644 --- a/lib/carto/functions.js +++ b/lib/carto/functions.js @@ -94,6 +94,13 @@ tree.functions = { return hsla(hsl); }, + replace: function (entity, a, b) { + if (entity.is === 'field') { + return entity.toString + '.replace(' + a.toString() + ', ' + b.toString() + ')'; + } else { + return entity.replace(a, b); + } + }, // // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein // http://sass-lang.com @@ -126,7 +133,7 @@ tree.functions = { .replace(/%[da]/, args[i].toString()); } str = str.replace(/%%/g, '%'); - return new tree.Quoted('"' + str + '"', str); + return new tree.Quoted(str); } }; diff --git a/lib/carto/index.js b/lib/carto/index.js index e1c72a9..9e73bcd 100644 --- a/lib/carto/index.js +++ b/lib/carto/index.js @@ -50,9 +50,9 @@ var carto = { } }; -[ 'anonymous', 'call', 'color', 'comment', 'definition', 'dimension', - 'directive', 'element', 'expression', 'filterset', 'filter', - 'keyword', 'layer', 'operation', 'quoted', 'imagefilter', +[ 'call', 'color', 'comment', 'definition', 'dimension', + 'directive', 'element', 'expression', 'filterset', 'filter', 'field', + 'keyword', 'layer', 'literal', 'operation', 'quoted', 'imagefilter', 'reference', 'rule', 'ruleset', 'selector', 'style', 'url', 'value', 'variable', 'zoom', 'invalid', 'fontset' ].forEach(function(n) { diff --git a/lib/carto/parser.js b/lib/carto/parser.js index d65afc2..733cae7 100644 --- a/lib/carto/parser.js +++ b/lib/carto/parser.js @@ -419,10 +419,24 @@ carto.Parser = function Parser(env) { if (input.charAt(i) !== '"' && input.charAt(i) !== "'") return; var str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); if (str) { - return new tree.Quoted(str[0], str[1] || str[2]); + return new tree.Quoted(str[1] || str[2]); } }, + // A reference to a Mapnik field, like + // + // [NAME] + // + // Behind the scenes, this has the same representation, but Carto + // needs to be careful to warn when unsupported operations are used. + field: function() { + if (! $('[')) return; + var field_name = $(/(^[a-zA-Z0-9\-_]+)/); + if (! $(']')) return; + if (field_name) return new tree.Field(field_name[1]); + }, + + // This is a comparison operator comparison: function() { var str = $(/^=~|=|!=|<=|>=|<|>/); if (str) { @@ -449,7 +463,7 @@ carto.Parser = function Parser(env) { call: function() { var name, args; - if (! (name = /^([\w-]+|%)\(/.exec(chunks[j]))) return; + if (! (name = /^([\w\-]+|%)\(/.exec(chunks[j]))) return; name = name[1].toLowerCase(); @@ -467,13 +481,15 @@ carto.Parser = function Parser(env) { return new tree.Call(name, args, i); } }, - arguments: function() { + // Arguments are comma-separated expressions + 'arguments': function() { var args = [], arg; while (arg = $(this.expression)) { args.push(arg); if (! $(',')) { break; } } + return args; }, literal: function() { @@ -497,7 +513,7 @@ carto.Parser = function Parser(env) { return new tree.Invalid(value, memo, 'Missing closing ) in URL.'); } else { return new tree.URL((value.value || value instanceof tree.Variable) ? - value : new tree.Anonymous(value), imports.paths); + value : new tree.Quoted(value), imports.paths); } }, @@ -553,7 +569,9 @@ carto.Parser = function Parser(env) { variable: function() { var name; - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1]; } + if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { + return name[1]; + } }, // @@ -561,8 +579,12 @@ carto.Parser = function Parser(env) { // and can be found inside a rule's value. // entity: function() { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword); + return $(this.entities.literal) || + $(this.entities.field) || + $(this.entities.variable) || + $(this.entities.url) || + $(this.entities.call) || + $(this.entities.keyword); }, // @@ -743,6 +765,7 @@ carto.Parser = function Parser(env) { return new tree.Value(expressions); } }, + // A sub-expression, contained by parenthensis sub: function() { var e; @@ -750,10 +773,12 @@ carto.Parser = function Parser(env) { return e; } }, + // This is a misnomer because it actually handles multiplication + // and division. multiplication: function() { var m, a, op, operation; if (m = $(this.operand)) { - while ((op = ($('/') || $('*'))) && (a = $(this.operand))) { + while ((op = ($('/') || $('*') || $('%'))) && (a = $(this.operand))) { operation = new tree.Operation(op, [operation || m, a], memo); } return operation || m; @@ -773,9 +798,7 @@ carto.Parser = function Parser(env) { // An operand is anything that can be part of an operation, // such as a Color, or a Variable operand: function() { - return $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); + return $(this.sub) || $(this.entity); }, // Expressions either represent mathematical operations, diff --git a/lib/carto/tree/call.js b/lib/carto/tree/call.js index 10015eb..d4bf72c 100644 --- a/lib/carto/tree/call.js +++ b/lib/carto/tree/call.js @@ -1,8 +1,5 @@ (function(tree) { -// -// A function call node. -// tree.Call = function Call(name, args, index) { this.name = name; this.args = args; @@ -50,9 +47,37 @@ tree.Call.prototype = { value: 'undefined' }; } - } else { // 2. - return new tree.Anonymous(this.name + - '(' + args.map(function(a) { return a.toString(); }).join(', ') + ')'); + } else { + var fn = tree.Reference.mapnikFunction(this.name); + if (!fn) { + env.error({ + message: 'unknown function ' + this.name, + index: this.index, + type: 'runtime', + filename: this.filename + }); + return { + is: 'undefined', + value: 'undefined' + }; + } + if (fn[1] !== args.length) { + env.error({ + message: 'function ' + this.name + ' takes ' + + fn[1] + ' arguments and was given ' + args.length, + index: this.index, + type: 'runtime', + filename: this.filename + }); + return { + is: 'undefined', + value: 'undefined' + }; + } else { + // Save the evaluated versions of arguments + this.args = args; + return this; + } } }, toString: function(env) { diff --git a/lib/carto/tree/comment.js b/lib/carto/tree/comment.js index 98385c2..f7e0d90 100644 --- a/lib/carto/tree/comment.js +++ b/lib/carto/tree/comment.js @@ -4,6 +4,7 @@ tree.Comment = function Comment(value, silent) { this.value = value; this.silent = !!silent; }; + tree.Comment.prototype = { toString: function(env) { return ''; diff --git a/lib/carto/tree/dimension.js b/lib/carto/tree/dimension.js index dfa7de2..0fe8458 100644 --- a/lib/carto/tree/dimension.js +++ b/lib/carto/tree/dimension.js @@ -25,7 +25,7 @@ tree.Dimension.prototype = { return new tree.Color([this.value, this.value, this.value]); }, toString: function() { - return this.value; + return this.value.toString(); }, // In an operation between two Dimensions, diff --git a/lib/carto/tree/field.js b/lib/carto/tree/field.js new file mode 100644 index 0000000..f6ebe8d --- /dev/null +++ b/lib/carto/tree/field.js @@ -0,0 +1,17 @@ +(function(tree) { + +tree.Field = function Field(content) { + this.value = content || ''; + this.is = 'field'; +}; + +tree.Field.prototype = { + toString: function() { + return '[' + this.value + ']'; + }, + 'eval': function() { + return this; + } +}; + +})(require('../tree')); diff --git a/lib/carto/tree/literal.js b/lib/carto/tree/literal.js new file mode 100644 index 0000000..135be6c --- /dev/null +++ b/lib/carto/tree/literal.js @@ -0,0 +1,20 @@ +// A literal is a literal string for Mapnik - the +// result of the combination of a `tree.Field` with any +// other type. +(function(tree) { + +tree.Literal = function Field(content) { + this.value = content || ''; + this.is = 'field'; +}; + +tree.Literal.prototype = { + toString: function() { + return this.value; + }, + 'eval': function() { + return this; + } +}; + +})(require('../tree')); diff --git a/lib/carto/tree/operation.js b/lib/carto/tree/operation.js index 8fd0e87..627063a 100644 --- a/lib/carto/tree/operation.js +++ b/lib/carto/tree/operation.js @@ -1,11 +1,12 @@ (function(tree) { - tree.Operation = function Operation(op, operands, index) { this.op = op.trim(); this.operands = operands; this.index = index; + this.is = 'operation'; }; + tree.Operation.prototype.eval = function(env) { var a = this.operands[0].eval(env), b = this.operands[1].eval(env), @@ -30,9 +31,11 @@ tree.Operation.prototype.eval = function(env) { } } - if (a instanceof tree.Quoted || b instanceof tree.Quoted) { + // Only concatenate plain strings, because this is easily + // pre-processed + if (a instanceof tree.Quoted && b instanceof tree.Quoted && this.op !== '+') { env.error({ - message: 'One cannot add, subtract, divide, or multiply strings.', + message: "Can't subtract, divide, or multiply strings.", index: this.index, type: 'runtime', filename: this.filename @@ -43,6 +46,25 @@ tree.Operation.prototype.eval = function(env) { }; } + // Fields, literals, dimensions, and quoted strings can be combined. + if (a instanceof tree.Field || b instanceof tree.Field || + a instanceof tree.Literal || b instanceof tree.Literal) { + if (a.is === 'color' || b.is === 'color') { + env.error({ + message: "Can't subtract, divide, or multiply colors in expressions.", + index: this.index, + type: 'runtime', + filename: this.filename + }); + return { + is: 'undefined', + value: 'undefined' + }; + } else { + return new tree.Literal(a.eval(env).toString(true) + this.op + b.eval(env).toString(true)); + } + } + return a.operate(this.op, b); }; @@ -51,6 +73,7 @@ tree.operate = function(op, a, b) { case '+': return a + b; case '-': return a - b; case '*': return a * b; + case '%': return a % b; case '/': return a / b; } }; diff --git a/lib/carto/tree/quoted.js b/lib/carto/tree/quoted.js index 9c30a3d..4064098 100644 --- a/lib/carto/tree/quoted.js +++ b/lib/carto/tree/quoted.js @@ -1,17 +1,23 @@ (function(tree) { -tree.Quoted = function Quoted(str, content) { +tree.Quoted = function Quoted(content) { this.value = content || ''; - this.quote = str.charAt(0); this.is = 'string'; }; + tree.Quoted.prototype = { toString: function(quotes) { var xmlvalue = this.value.replace(/\'/g, '''); return (quotes === true) ? "'" + xmlvalue + "'" : this.value; }, + 'eval': function() { return this; + }, + + operate: function(op, other) { + return new tree.Quoted(true, + tree.operate(op, this.toString(), other.toString(this.contains_field))); } }; diff --git a/lib/carto/tree/reference.js b/lib/carto/tree/reference.js index bdc212d..e46622b 100644 --- a/lib/carto/tree/reference.js +++ b/lib/carto/tree/reference.js @@ -1,3 +1,9 @@ +/* + * Carto pulls in a reference from the `mapnik-reference` + * module. This file builds indexes from that file for its various + * options, and provides validation methods for property: value + * combinations. + */ (function(tree) { var _ = require('underscore'); @@ -59,6 +65,24 @@ tree.Reference.symbolizer = function(selector) { } }; +/* + * For transform properties and image-filters, + * mapnik has its own functions. + */ +tree.Reference.mapnikFunction = function(name) { + 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); + } + } + } + return _.find(functions, 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]; @@ -101,28 +125,30 @@ tree.Reference.isFont = function(selector) { tree.Reference.validValue = function(env, selector, value) { var i, j; - if (value[0]) { - return tree.Reference.selector(selector).type == value[0].is; - } else { - // TODO: handle in reusable way - if (!tree.Reference.selector(selector)) { - return false; - } else if (value.value[0].is == 'keyword') { - return tree.Reference - .selector(selector).type - .indexOf(value.value[0].value) !== -1; - } else if (value.value[0].is == 'undefined') { - // caught earlier in the chain - ignore here so that - // error is not overridden - return true; - } else if (tree.Reference.selector(selector).type == 'numbers') { - for (i in value.value) { - if (value.value[i].is !== 'float') { - return false; - } + // TODO: handle in reusable way + if (!tree.Reference.selector(selector)) { + return false; + } else if (value.value[0].is == 'keyword') { + return tree.Reference + .selector(selector).type + .indexOf(value.value[0].value) !== -1; + } else if (value.value[0].is == 'undefined') { + // caught earlier in the chain - ignore here so that + // error is not overridden + return true; + } else if (tree.Reference.selector(selector).type == 'numbers') { + for (i in value.value) { + if (value.value[i].is !== 'float') { + return false; } + } + return true; + } else if (tree.Reference.selector(selector).type == 'functions') { + // For backwards compatibility, you can specify a string for `functions`-compatible + // values, though they will not be validated. + if (value.value[0].is === 'string') { return true; - } else if (tree.Reference.selector(selector).type == 'functions') { + } else { for (i in value.value) { for (j in value.value[i].value) { if (value.value[i].value[j].is !== 'call') { @@ -139,22 +165,24 @@ tree.Reference.validValue = function(env, selector, value) { } } return true; - } else { - if (tree.Reference.selector(selector).validate) { - var valid = false; - for (i = 0; i < value.value.length; i++) { - if (tree.Reference.selector(selector).type == value.value[i].is && - tree.Reference - ._validateValue - [tree.Reference.selector(selector).validate] - (env, value.value[i].value)) { - return true; - } + } + } else if (tree.Reference.selector(selector).type == 'expression') { + return true; + } else { + if (tree.Reference.selector(selector).validate) { + var valid = false; + for (i = 0; i < value.value.length; i++) { + if (tree.Reference.selector(selector).type == value.value[i].is && + tree.Reference + ._validateValue + [tree.Reference.selector(selector).validate] + (env, value.value[i].value)) { + return true; } - return valid; - } else { - return tree.Reference.selector(selector).type == value.value[0].is; } + return valid; + } else { + return tree.Reference.selector(selector).type == value.value[0].is; } } }; diff --git a/package.json b/package.json index f07837e..35ac355 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ }, "dependencies": { "underscore": "~1.3.3", - "mapnik-reference": "~2.1.0", + "mapnik-reference": "https://github.com/mapnik/mapnik-reference/tarball/transform-functions", "xml2js": "~0.1.13" }, "devDependencies": { diff --git a/test/rendering/building_height.mml b/test/rendering/building_height.mml new file mode 100644 index 0000000..79ba517 --- /dev/null +++ b/test/rendering/building_height.mml @@ -0,0 +1,23 @@ +{ + "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": [ + "building_height.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" + } + }, + { + "class": "new", + "name": "countries", + "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" + } + }] +} diff --git a/test/rendering/building_height.mss b/test/rendering/building_height.mss new file mode 100644 index 0000000..6122c84 --- /dev/null +++ b/test/rendering/building_height.mss @@ -0,0 +1,4 @@ +@n: 4; +#world { + building-height: 2 * 3 * [HEIGHT] + 2 + [NOTHEIGHT] + (@n * 2); +} diff --git a/test/rendering/building_height.result b/test/rendering/building_height.result new file mode 100644 index 0000000..393778d --- /dev/null +++ b/test/rendering/building_height.result @@ -0,0 +1,20 @@ + + + + + + + + world + + + + + + + diff --git a/test/rendering/field.mml b/test/rendering/field.mml new file mode 100644 index 0000000..94629dc --- /dev/null +++ b/test/rendering/field.mml @@ -0,0 +1,23 @@ +{ + "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": [ + "field.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" + } + }, + { + "class": "new", + "name": "countries", + "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" + } + }] +} diff --git a/test/rendering/field.mss b/test/rendering/field.mss new file mode 100644 index 0000000..5cd939b --- /dev/null +++ b/test/rendering/field.mss @@ -0,0 +1,6 @@ +#world { + text-name: "hello " + [NAME] + " hello"; + text-size: 11; + text-face-name: "Georgia Regular", "Arial Italic"; +} + diff --git a/test/rendering/field.result b/test/rendering/field.result new file mode 100644 index 0000000..716e378 --- /dev/null +++ b/test/rendering/field.result @@ -0,0 +1,23 @@ + + + + + + + + + + + world + + + + + + + diff --git a/test/rendering/field_advanced.mml b/test/rendering/field_advanced.mml new file mode 100644 index 0000000..12153a4 --- /dev/null +++ b/test/rendering/field_advanced.mml @@ -0,0 +1,23 @@ +{ + "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": [ + "field_advanced.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" + } + }, + { + "class": "new", + "name": "countries", + "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" + } + }] +} diff --git a/test/rendering/field_advanced.mss b/test/rendering/field_advanced.mss new file mode 100644 index 0000000..68f41e8 --- /dev/null +++ b/test/rendering/field_advanced.mss @@ -0,0 +1,6 @@ +#world { + text-name: "hello " + [NAME] + " hello" + 2; + text-size: 11; + text-face-name: "Georgia Regular", "Arial Italic"; +} + diff --git a/test/rendering/field_advanced.result b/test/rendering/field_advanced.result new file mode 100644 index 0000000..cf47ac3 --- /dev/null +++ b/test/rendering/field_advanced.result @@ -0,0 +1,23 @@ + + + + + + + + + + + world + + + + + + + diff --git a/test/rendering/modulus.mml b/test/rendering/modulus.mml new file mode 100644 index 0000000..e46eb87 --- /dev/null +++ b/test/rendering/modulus.mml @@ -0,0 +1,23 @@ +{ + "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": [ + "modulus.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" + } + }, + { + "class": "new", + "name": "countries", + "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" + } + }] +} diff --git a/test/rendering/modulus.mss b/test/rendering/modulus.mss new file mode 100644 index 0000000..e9d797d --- /dev/null +++ b/test/rendering/modulus.mss @@ -0,0 +1,6 @@ +#world { + text-name: "hello "; + text-size: 11 % 2; + text-face-name: "Georgia Regular", "Arial Italic"; +} + diff --git a/test/rendering/modulus.result b/test/rendering/modulus.result new file mode 100644 index 0000000..b0f7020 --- /dev/null +++ b/test/rendering/modulus.result @@ -0,0 +1,23 @@ + + + + + + + + + + + world + + + + + + + diff --git a/test/rendering/transforms.mss b/test/rendering/transforms.mss new file mode 100644 index 0000000..628b984 --- /dev/null +++ b/test/rendering/transforms.mss @@ -0,0 +1,5 @@ +@trans: 2; +#world { + point-file: url(foo.png); + point-transform: translate(@trans * 2, @trans); +} diff --git a/test/rendering/transforms.result b/test/rendering/transforms.result new file mode 100644 index 0000000..80c5961 --- /dev/null +++ b/test/rendering/transforms.result @@ -0,0 +1,20 @@ + + + + + + + + world + + + + + + + diff --git a/test/rendering/transforms_backwards.mml b/test/rendering/transforms_backwards.mml new file mode 100644 index 0000000..b901d36 --- /dev/null +++ b/test/rendering/transforms_backwards.mml @@ -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": [ + "transforms_backwards.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" + } + }] +} diff --git a/test/rendering/transforms_backwards.mss b/test/rendering/transforms_backwards.mss new file mode 100644 index 0000000..f7d75e1 --- /dev/null +++ b/test/rendering/transforms_backwards.mss @@ -0,0 +1,4 @@ +#world { + point-file: url(foo.png); + point-transform: "translate(2, 2)"; +} diff --git a/test/rendering/transforms_backwards.result b/test/rendering/transforms_backwards.result new file mode 100644 index 0000000..e472fba --- /dev/null +++ b/test/rendering/transforms_backwards.result @@ -0,0 +1,20 @@ + + + + + + + + world + + + + + + + diff --git a/test/rendering/transforms_field.mml b/test/rendering/transforms_field.mml new file mode 100644 index 0000000..57fdb81 --- /dev/null +++ b/test/rendering/transforms_field.mml @@ -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": [ + "transforms_field.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" + } + }] +} diff --git a/test/rendering/transforms_field.mss b/test/rendering/transforms_field.mss new file mode 100644 index 0000000..834051b --- /dev/null +++ b/test/rendering/transforms_field.mss @@ -0,0 +1,4 @@ +#world { + point-file:url(foo.png); + point-transform: scale(2 * 5 * [POP2005], [POP2005]); +} diff --git a/test/rendering/transforms_field.result b/test/rendering/transforms_field.result new file mode 100644 index 0000000..cb89a8a --- /dev/null +++ b/test/rendering/transforms_field.result @@ -0,0 +1,20 @@ + + + + + + + + world + + + + + + +