234 lines
6.9 KiB
JavaScript
Executable File
234 lines
6.9 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
var sax = require('sax'),
|
|
fs = require('fs'),
|
|
_ = require('underscore')._,
|
|
path = require('path'),
|
|
sys = require('sys');
|
|
|
|
require.paths.unshift(path.join(__dirname, '../lib'), path.join(__dirname, '../lib/node'));
|
|
|
|
var carto = require('carto'),
|
|
args = process.argv.slice(1);
|
|
|
|
var options = {
|
|
silent: false,
|
|
json: false
|
|
};
|
|
|
|
xml2tree = function(xml, callback) {
|
|
var parser = sax.parser(true);
|
|
var tree = [ {} ];
|
|
parser.onopentag = function(node) {
|
|
if (!(node.name in tree[0])) tree[0][node.name] = [];
|
|
tree[0][node.name].push(node.attributes);
|
|
tree.unshift(node.attributes);
|
|
};
|
|
|
|
parser.onclosetag = function() {
|
|
tree.shift();
|
|
if (tree.length === 1) callback(tree[0]);
|
|
};
|
|
|
|
parser.ontext = parser.oncdata = function(text) {
|
|
if (text.trim()) tree[0].text = (tree[0].text || '') + text;
|
|
};
|
|
|
|
parser.write(xml.toString());
|
|
}
|
|
|
|
args = args.filter(function (arg) {
|
|
var match;
|
|
|
|
if (match = arg.match(/^--?([a-z][0-9a-z-]*)$/i)) { arg = match[1] }
|
|
else { return arg }
|
|
|
|
switch (arg) {
|
|
case 'h':
|
|
case 'help':
|
|
sys.puts("Usage: cartox source");
|
|
process.exit(0);
|
|
break;
|
|
}
|
|
});
|
|
|
|
var input = args[1];
|
|
if (input && input[0] != '/') {
|
|
input = path.join(process.cwd(), input);
|
|
}
|
|
|
|
if (!input) {
|
|
sys.puts("cartox: no input files");
|
|
process.exit(1);
|
|
}
|
|
|
|
var output = args[2];
|
|
if (output && output[0] != '/') {
|
|
output = path.join(process.cwd(), output);
|
|
}
|
|
|
|
function upStyle(s) {
|
|
this.name = s.name;
|
|
this.rules = [];
|
|
}
|
|
|
|
upStyle.prototype.toMSS = function() {
|
|
return '.' + this.name
|
|
+ ' {\n'
|
|
+ this.rules.join('\n')
|
|
+ '\n}';
|
|
};
|
|
|
|
function upRule(xmlRule) {
|
|
this.filters = xmlRule.Filter ? this.upFilter(xmlRule.Filter[0].text) : [];
|
|
this.zooms = (xmlRule.MaxScaleDenominator || xmlRule.MinScaleDenomintor)
|
|
? this.upZoom(xmlRule) : [];
|
|
this.rules = this.upSymbolizers(xmlRule);
|
|
}
|
|
|
|
upRule.prototype.upZoom = function(xmlRule) {
|
|
var zoomFilters = [];
|
|
var findZoom = function(denom) {
|
|
for (var i in carto.tree.Zoom.ranges) {
|
|
if (carto.tree.Zoom.ranges[i] == denom) return i;
|
|
}
|
|
};
|
|
|
|
if (xmlRule.MaxScaleDenominator) {
|
|
zoomFilters.push('[zoom >= ' + findZoom(xmlRule.MaxScaleDenominator[0].text) + ']');
|
|
}
|
|
if (xmlRule.MinScaleDenominator) {
|
|
zoomFilters.push('[zoom <= ' + findZoom(xmlRule.MinScaleDenominator[0].text) + ']');
|
|
}
|
|
return zoomFilters;
|
|
};
|
|
|
|
upRule.prototype.upFilter = function(xmlFilter) {
|
|
var filters = [];
|
|
var thisfilter = [];
|
|
var invert = false;
|
|
var xmlTok = xmlFilter.split(/\s+/);
|
|
var curTok = '';
|
|
for (var i in xmlTok) {
|
|
curTok = xmlTok[i].replace(/(\[|\]|\)|\()/g, '');
|
|
if (curTok == 'and') {
|
|
filters.push(thisfilter);
|
|
thisfilter = [];
|
|
} else if (curTok == 'not') {
|
|
invert = true;
|
|
} else if (invert && (['='].indexOf(curTok) !== -1)) {
|
|
thisfilter.push('!=');
|
|
invert = false;
|
|
} else {
|
|
thisfilter.push(curTok);
|
|
}
|
|
}
|
|
if (thisfilter) filters.push(thisfilter);
|
|
return filters.map(function(f) {
|
|
return f.join(' ');
|
|
});
|
|
};
|
|
|
|
upRule.prototype.upSymbolizers = function(xmlRule) {
|
|
var css_rules = [];
|
|
var symnames = _.map(_.keys(carto.tree.Reference.data.symbolizers), function(symbolizer) {
|
|
return [symbolizer.charAt(0).toUpperCase() +
|
|
symbolizer.slice(1).replace(/\-./, function(str) {
|
|
return str[1].toUpperCase();
|
|
}) + 'Symbolizer', carto.tree.Reference.data.symbolizers[symbolizer]];
|
|
});
|
|
var symmap = _.reduce(symnames, function(memo, s) {
|
|
memo[s[0]] = s[1];
|
|
return memo;
|
|
}, {});
|
|
var cssmap = function(symbolizer, name) {
|
|
return symmap[symbolizer][name].css;
|
|
}
|
|
for (var i in xmlRule) {
|
|
if (i in symmap) {
|
|
for (var j in xmlRule[i][0]) {
|
|
if (j == 'CssParameter') {
|
|
sys.error("cartox: no support for CssParameter, please upgrade your xml to Mapnik 2.0 syntax first");
|
|
process.exit(1);
|
|
}
|
|
if (!symmap[i][j]) {
|
|
sys.error('Property ' + j + ' not yet supported');
|
|
} else if (symmap[i][j].type == 'uri') {
|
|
css_rules.push(cssmap(i, j) + ': url("' + xmlRule[i][0][j] + '");');
|
|
} else if (['float', 'color', 'boolean', 'numbers'].indexOf(symmap[i][j].type) !== -1 || _.isArray(symmap[i][j].type)) {
|
|
css_rules.push(cssmap(i, j) + ': ' + xmlRule[i][0][j] + ';');
|
|
} else {
|
|
css_rules.push(cssmap(i, j) + ': "' + xmlRule[i][0][j] + '";');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return css_rules;
|
|
};
|
|
|
|
|
|
upRule.prototype.toMSS = function() {
|
|
if (this.filters.length || this.zooms.length) {
|
|
return ' ' + this.filters.map(function(f) {
|
|
return '[' + f + ']';
|
|
}).join('') +
|
|
this.zooms.map(function(f) {
|
|
return f;
|
|
}).join('') +
|
|
' {\n '
|
|
+ this.rules.join('\n ')
|
|
+ '\n }\n';
|
|
} else {
|
|
' ' + this.rules.join('\n ');
|
|
}
|
|
}
|
|
|
|
function upDatasource(ds) {
|
|
var params = {};
|
|
ds[0].Parameter.forEach(function(param) {
|
|
params[param.name] = param.text;
|
|
});
|
|
return params;
|
|
}
|
|
|
|
fs.readFile(input, 'utf-8', function (e, data) {
|
|
var styles = [];
|
|
var document = {};
|
|
var layers = [];
|
|
if (e) throw e;
|
|
xml2tree(data, function(mapnik_xml) {
|
|
mapnik_xml.Map[0].Style.forEach(function(s) {
|
|
var newStyle = new upStyle(s);
|
|
s.Rule.forEach(function(r) {
|
|
newStyle.rules.push((new upRule(r)).toMSS());
|
|
});
|
|
styles.push(newStyle.toMSS());
|
|
});
|
|
styles = styles.reverse();
|
|
mapnik_xml.Map[0].Layer.forEach(function(l) {
|
|
var additional_classes = l.StyleName ? ' ' + l.StyleName.map(function(s) { return s.text; }).join(' ') : '';
|
|
var newLayer = {
|
|
name: l.name,
|
|
class: l.name + '_style' + additional_classes,
|
|
srs: l.srs
|
|
};
|
|
newLayer.Datasource = upDatasource(l.Datasource);
|
|
layers.push(newLayer);
|
|
});
|
|
if (output) {
|
|
document.Stylesheet = [output.replace('mml', 'mss')];
|
|
document.Layer = layers;
|
|
fs.writeFile(output, JSON.stringify(document), 'utf8', function(err) {
|
|
sys.error('write MML');
|
|
});
|
|
fs.writeFile(output.replace('mml', 'mss'), styles.join('\n\n'), 'utf8', function(err) {
|
|
sys.error('write MSS');
|
|
});
|
|
} else {
|
|
document.Stylesheet = [{ id: 'gen', data: styles.join('\n') }];
|
|
document.Layer = layers;
|
|
console.log(JSON.stringify(document));
|
|
}
|
|
});
|
|
});
|