From 9e7e28411a07a35202a2223d0d7beccfe0b6263c Mon Sep 17 00:00:00 2001 From: nebulon42 Date: Sat, 27 Feb 2016 20:19:38 +0100 Subject: [PATCH] add support for targeting mapnik api versions from the command line and support minimum/maximum-scale-denominiator (fixes #394) --- README.md | 9 ++++++++ bin/carto | 22 ++++++++++++++++++-- lib/carto/tree/layer.js | 20 ++++++++++++++++-- lib/carto/tree/reference.js | 9 +++++++- package.json | 1 + test/bincarto.test.js | 9 ++++++++ test/rendering.test.js | 26 +++++++++++++++++++++--- test/rendering/issue_394.mss | 4 ++++ test/rendering/issue_394_api2.3.0.mml | 18 ++++++++++++++++ test/rendering/issue_394_api2.3.0.result | 22 ++++++++++++++++++++ test/rendering/issue_394_api3.0.0.mml | 18 ++++++++++++++++ test/rendering/issue_394_api3.0.0.result | 22 ++++++++++++++++++++ 12 files changed, 172 insertions(+), 8 deletions(-) create mode 100644 test/rendering/issue_394.mss create mode 100644 test/rendering/issue_394_api2.3.0.mml create mode 100644 test/rendering/issue_394_api2.3.0.result create mode 100644 test/rendering/issue_394_api3.0.0.mml create mode 100644 test/rendering/issue_394_api3.0.0.result diff --git a/README.md b/README.md index aa01dd37d..a40159d38 100644 --- a/README.md +++ b/README.md @@ -195,6 +195,15 @@ Now that Carto is installed you should have a `carto` command line tool availabl carto project.mml > mapnik.xml +Available parameters: +* -h / --help - Display help message +* -v / --version - Display version information +* -b / --benchmark - Outputs total compile time +* -l / --localize - Use millstone to localize resources when loading an MML (default: off) +* -n / --nosymlink - Use absolute paths instead of symlinking files +* -a / --api VERSION - Specify Mapnik API version (e.g. --api 3.0.10) (default: 2.3.0) +* -ppi RESOLUTION - Pixels per inch used to convert m, mm, cm, in, pt, pc to pixels (default: 90.714) + #### From code Currently CartoCSS is designed to be invoked from [node.js](http://nodejs.org/). diff --git a/bin/carto b/bin/carto index 962543f10..5f75e74d8 100755 --- a/bin/carto +++ b/bin/carto @@ -3,6 +3,7 @@ var path = require('path'), fs = require('fs'), carto = require('../lib/carto'), + semver = require('semver'), url = require('url'), _ = require('lodash'), yaml = require('js-yaml'); @@ -16,6 +17,7 @@ var yargs = require('yargs') .options('b', {alias:'benchmark', boolean:true, describe:'Outputs total compile time'}) .options('l', {alias:'localize', boolean:true, default:false, describe:'Use millstone to localize resources when loading an MML'}) .options('n', {alias:'nosymlink', boolean:true, describe:'Use absolute paths instead of symlinking files'}) + .options('a', {alias:'api', describe:'Specify Mapnik API version', default:'2.3.0'}) .options('ppi', {describe:'Pixels per inch used to convert m, mm, cm, in, pt, pc to pixels', default:90.714}); var options = yargs.argv; @@ -40,6 +42,13 @@ if (!input) { process.exit(1); } +if (options.api) { + if (!semver.valid(options.api)) { + console.error("carto: invalid Mapnik API version. A valid version is e.g. 3.0.10"); + process.exit(1); + } +} + if (options.benchmark) { var start = +new Date; } @@ -70,13 +79,19 @@ function compileMML(err, data) { filename: input, benchmark: options.benchmark, ppi: options.ppi + }, + { + mapnik_version: options.api }); try { var output = renderer.render(data); } catch (e) { if (e.stack) { console.error(e.stack); - } else { + } else if (e.message) { + console.error(e.message); + } + else { console.error(e); } process.exit(1); @@ -98,7 +113,10 @@ function compileMSS(err, data) { filename: path.basename(input), benchmark: options.benchmark, ppi: options.ppi - }); + }, + { + mapnik_version: options.api + }); try { var output = renderer.renderMSS(data); } catch (e) { diff --git a/lib/carto/tree/layer.js b/lib/carto/tree/layer.js index 24e61ce93..3972051cb 100644 --- a/lib/carto/tree/layer.js +++ b/lib/carto/tree/layer.js @@ -1,3 +1,5 @@ +var semver = require('semver'); + (function(tree) { tree.LayerXML = function(obj, styles) { @@ -7,12 +9,26 @@ tree.LayerXML = function(obj, styles) { obj.Datasource[i] + ']]>'); } + var apiVersion = tree.Reference.data['version']; + var prop_string = ''; for (var prop in obj.properties) { if (prop === 'minzoom') { - prop_string += ' maxzoom="' + tree.Zoom.ranges[obj.properties[prop]] + '"\n'; + if (semver.gte(apiVersion, '3.0.0')) { + prop_string += ' maximum-scale-denominator="' + } + else { + prop_string += ' maxzoom="' + } + prop_string += tree.Zoom.ranges[obj.properties[prop]] + '"\n'; } else if (prop === 'maxzoom') { - prop_string += ' minzoom="' + tree.Zoom.ranges[obj.properties[prop]+1] + '"\n'; + if (semver.gte(apiVersion, '3.0.0')) { + prop_string += ' minimum-scale-denominator="' + } + else { + prop_string += ' minzoom="' + } + prop_string += tree.Zoom.ranges[obj.properties[prop]+1] + '"\n'; } else { prop_string += ' ' + prop + '="' + obj.properties[prop] + '"\n'; } diff --git a/lib/carto/tree/reference.js b/lib/carto/tree/reference.js index 0fa30bdf7..963fa7232 100644 --- a/lib/carto/tree/reference.js +++ b/lib/carto/tree/reference.js @@ -24,7 +24,14 @@ ref.setData = function(data) { }; ref.setVersion = function(version) { - ref.setData(mapnik_reference.load(version)); + try { + ref.setData(mapnik_reference.load(version)); + } + catch(err) { + var e = new Error('Mapnik version ' + version + ' is not supported'); + e.stack = null; // do not show stack trace + throw e; + } }; ref.selectorData = function(selector, i) { diff --git a/package.json b/package.json index 724e938ed..5e79ddd39 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "js-yaml": "^3.4.6", "lodash": "^4.5.1", "mapnik-reference": "~8.5.3", + "semver": "^5.1.0", "yargs": "^4.2.0" }, "devDependencies": { diff --git a/test/bincarto.test.js b/test/bincarto.test.js index 78e125713..5e9a98f6a 100644 --- a/test/bincarto.test.js +++ b/test/bincarto.test.js @@ -14,6 +14,15 @@ describe('bin/carto', function() { done(); }); }); + it('errors on unsupported api version', function(done) { + var file = path.join('test', 'rendering', 'identity.mml'); + var api = '1.0.0'; + exec(util.format('node %s -a %s %s', bin, api, file), function(err, stdout, stderr) { + assert.equal(1, err.code); + assert.equal("Mapnik version 1.0.0 is not supported\n", stderr); + done(); + }); + }); it('renders mml', function(done) { var file = path.join('test', 'rendering', 'identity.mml'); exec(util.format('node %s %s', bin, file), function(err, stdout, stderr) { diff --git a/test/rendering.test.js b/test/rendering.test.js index 17a548bb6..14dfa4b69 100644 --- a/test/rendering.test.js +++ b/test/rendering.test.js @@ -1,6 +1,7 @@ var path = require('path'), assert = require('assert'), - fs = require('fs'); + fs = require('fs'), + semver = require('semver'); var carto = require('../lib/carto'); var tree = require('../lib/carto/tree'); @@ -8,17 +9,36 @@ var helper = require('./support/helper'); describe('Rendering', function() { helper.files('rendering', 'mml', function(file) { + var api = null, + filename = path.basename(file); + if (filename.indexOf('_api') !== -1) { + api = filename.substring(filename.indexOf('_api') + 4, filename.length - 4); + if (!semver.valid(api)) { + api = null; + } + } it('should render ' + path.basename(file) + ' correctly', function(done) { var completed = false; var renderResult; var mml = helper.mml(file); try { - var output = new carto.Renderer({ + var env = { paths: [ path.dirname(file) ], data_dir: path.join(__dirname, '../data'), local_data_dir: path.join(__dirname, 'rendering'), filename: file - }).render(mml); + }, + renderer = null; + + if (api) { + renderer = new carto.Renderer(env, { + mapnik_version: api + }); + } + else { + renderer = new carto.Renderer(env); + } + var output = renderer.render(mml); } catch(err) { if (Array.isArray(err)){ err.forEach(carto.writeError); diff --git a/test/rendering/issue_394.mss b/test/rendering/issue_394.mss new file mode 100644 index 000000000..e1f1a556f --- /dev/null +++ b/test/rendering/issue_394.mss @@ -0,0 +1,4 @@ +#world { + line-width: 2; + line-color: #024; +} diff --git a/test/rendering/issue_394_api2.3.0.mml b/test/rendering/issue_394_api2.3.0.mml new file mode 100644 index 000000000..989349ea3 --- /dev/null +++ b/test/rendering/issue_394_api2.3.0.mml @@ -0,0 +1,18 @@ +{ + "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": [ + "issue_394.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" + }, + "properties": { + "minzoom": 7, + "maxzoom": 9 + } + }] +} diff --git a/test/rendering/issue_394_api2.3.0.result b/test/rendering/issue_394_api2.3.0.result new file mode 100644 index 000000000..cc6aaea9e --- /dev/null +++ b/test/rendering/issue_394_api2.3.0.result @@ -0,0 +1,22 @@ + + + + + + + + world + + + + + + + diff --git a/test/rendering/issue_394_api3.0.0.mml b/test/rendering/issue_394_api3.0.0.mml new file mode 100644 index 000000000..989349ea3 --- /dev/null +++ b/test/rendering/issue_394_api3.0.0.mml @@ -0,0 +1,18 @@ +{ + "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": [ + "issue_394.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" + }, + "properties": { + "minzoom": 7, + "maxzoom": 9 + } + }] +} diff --git a/test/rendering/issue_394_api3.0.0.result b/test/rendering/issue_394_api3.0.0.result new file mode 100644 index 000000000..1e89b009b --- /dev/null +++ b/test/rendering/issue_394_api3.0.0.result @@ -0,0 +1,22 @@ + + + + + + + + world + + + + + + +