UNPKG

carto

Version:

Mapnik Stylesheet Compiler

336 lines (302 loc) 11.4 kB
var chroma = require('chroma-js'), hsluv = require('hsluv'), _ = require('lodash'); (function (tree) { tree.functions = { rgb: function (r, g, b) { return this.rgba(r, g, b, 1.0); }, rgba: function (r, g, b, a) { var rgb = [r, g, b].map(function (c) { return number(c); }); a = number(a); if (rgb.some(isNaN) || isNaN(a)) return null; this.rgb[0] = Math.max(0, Math.min(this.rgb[0], 255)); this.rgb[1] = Math.max(0, Math.min(this.rgb[1], 255)); this.rgb[2] = Math.max(0, Math.min(this.rgb[2], 255)); var hsl = chroma(rgb).hsl(); if (isNaN(hsl[0])) { hsl[0] = 0; } return new tree.Color(hsl, a); }, // Only require val stop: function (val) { var color, mode; if (arguments.length > 1) color = arguments[1]; if (arguments.length > 2) mode = arguments[2]; return { is: 'tag', val: val, color: color, mode: mode, toString: function(env) { var attr = {}; _.set(attr, 'value', val.ev(env).toString()); if (color) { _.set(attr, 'color', color.ev(env).toString()); } if (mode) { _.set(attr, 'mode', mode.ev(env).toString()); } return { '_name': 'stop', '_attributes': attr }; } }; }, hsl: function (h, s, l) { return this.hsla(h, s, l, 1.0); }, hsla: function (h, s, l, a) { var hsl = [h, s, l].map(function (c) { return number(c); }); a = number(a); if (hsl.some(isNaN) || isNaN(a)) return null; return new tree.Color(hsl, a, false); }, hsluv: function (h, s, l) { return this.hsluva(h, s, l, 1.0); }, hsluva: function (h, s, l, a) { var hsl = [h, s, l].map(function (c) { return number(c); }); a = number(a); if (hsl.some(isNaN) || isNaN(a)) return null; return new tree.Color(hsl, a, true); }, hue: function (color) { if (!('getComponents' in color)) return null; var comp = color.getComponents(); if (comp.perceptual) { if (!('toStandard' in color)) return null; color = color.toStandard(); comp = color.getComponents(); } return new tree.Dimension(Math.round(comp.h)); }, huep: function (color) { if (!('getComponents' in color)) return null; var comp = color.getComponents(); if (!comp.perceptual) { if (!('toPerceptual' in color)) return null; color = color.toPerceptual(); comp = color.getComponents(); } return new tree.Dimension(Math.round(comp.h)); }, saturation: function (color) { if (!('getComponents' in color)) return null; var comp = color.getComponents(); if (comp.perceptual) { if (!('toStandard' in color)) return null; color = color.toStandard(); comp = color.getComponents(); } return new tree.Dimension(Math.round(comp.s * 100), '%'); }, saturationp: function (color) { if (!('getComponents' in color)) return null; var comp = color.getComponents(); if (!comp.perceptual) { if (!('toPerceptual' in color)) return null; color = color.toPerceptual(); comp = color.getComponents(); } return new tree.Dimension(Math.round(comp.s * 100), '%'); }, lightness: function (color) { if (!('getComponents' in color)) return null; var comp = color.getComponents(); if (comp.perceptual) { if (!('toStandard' in color)) return null; color = color.toStandard(); comp = color.getComponents(); } return new tree.Dimension(Math.round(comp.l * 100), '%'); }, lightnessp: function (color) { if (!('getComponents' in color)) return null; var comp = color.getComponents(); if (!comp.perceptual) { if (!('toPerceptual' in color)) return null; color = color.toPerceptual(); comp = color.getComponents(); } return new tree.Dimension(Math.round(comp.l * 100), '%'); }, alpha: function (color) { if (!('getComponents' in color)) return null; var comp = color.getComponents(); return new tree.Dimension(comp.a); }, saturate: function (color, amount) { if (!('getComponents' in color)) return null; var comp = color.getComponents(); comp.s += amount.value / 100; comp.s = clamp(comp.s); return new tree.Color([comp.h, comp.s, comp.l], comp.a, comp.perceptual); }, saturatep: function (color, amount) { if (!('toPerceptual' in color)) return null; return this.saturate(color.toPerceptual(), amount); }, desaturate: function (color, amount) { if (!('getComponents' in color)) return null; var comp = color.getComponents(); comp.s -= amount.value / 100; comp.s = clamp(comp.s); return new tree.Color([comp.h, comp.s, comp.l], comp.a, comp.perceptual); }, desaturatep: function (color, amount) { if (!('toPerceptual' in color)) return null; return this.desaturate(color.toPerceptual(), amount); }, lighten: function (color, amount) { if (!('getComponents' in color)) return null; var comp = color.getComponents(); comp.l += amount.value / 100; comp.l = clamp(comp.l); return new tree.Color([comp.h, comp.s, comp.l], comp.a, comp.perceptual); }, lightenp: function (color, amount) { if (!('toPerceptual' in color)) return null; return this.lighten(color.toPerceptual(), amount); }, darken: function (color, amount) { if (!('getComponents' in color)) return null; var comp = color.getComponents(); comp.l -= amount.value / 100; comp.l = clamp(comp.l); return new tree.Color([comp.h, comp.s, comp.l], comp.a, comp.perceptual); }, darkenp: function (color, amount) { if (!('toPerceptual' in color)) return null; return this.darken(color.toPerceptual(), amount); }, fadein: function (color, amount) { if (!('getComponents' in color)) return null; var comp = color.getComponents(); comp.a += amount.value / 100; comp.a = clamp(comp.a); return new tree.Color([comp.h, comp.s, comp.l], comp.a, comp.perceptual); }, fadeinp: function (color, amount) { if (!('toPerceptual' in color)) return null; return this.fadein(color.toPerceptual(), amount); }, fadeout: function (color, amount) { if (!('getComponents' in color)) return null; var comp = color.getComponents(); comp.a -= amount.value / 100; comp.a = clamp(comp.a); return new tree.Color([comp.h, comp.s, comp.l], comp.a, comp.perceptual); }, fadeoutp: function (color, amount) { if (!('toPerceptual' in color)) return null; return this.fadeout(color.toPerceptual(), amount); }, spin: function (color, amount) { if (!('getComponents' in color)) return null; var comp = color.getComponents(); var hue = (comp.h + amount.value) % 360; comp.h = hue < 0 ? 360 + hue : hue; return new tree.Color([comp.h, comp.s, comp.l], comp.a, comp.perceptual); }, spinp: function (color, amount) { if (!('toPerceptual' in color)) return null; return this.spin(color.toPerceptual(), amount); }, replace: function (entity, a, b) { if (entity.is === 'field') { return entity.toString + '.replace(' + a.toString() + ', ' + b.toString() + ')'; } else { return entity.replace(a, b); } }, mix: function (color1, color2, weight) { if (!('isPerceptual' in color1) || !('isPerceptual' in color2) || !('toString' in color1) || !('toString' in color2) || !('getComponents' in color1) || !('getComponents' in color2)) return null; var perceptual = color1.isPerceptual() || color2.isPerceptual(); if (color1.isPerceptual() && !color2.isPerceptual()) { color2 = color2.toPerceptual(); } else if (!color1.toPerceptual() && color2.toPerceptual()) { color1 = color1.toPerceptual(); } var rgb1 = chroma(color1.toString()).rgb(); var rgb2 = chroma(color2.toString()).rgb(); var p = weight.value / 100.0; var alpha = color1.getComponents().a * p + color2.getComponents().a * (1 - p); var a = color1.getComponents().a - color2.getComponents().a; var w = p * 2 - 1; var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; var w2 = 1 - w1; var rgb = [rgb1[0] * w1 + rgb2[0] * w2, rgb1[1] * w1 + rgb2[1] * w2, rgb1[2] * w1 + rgb2[2] * w2]; var mix = null; if (perceptual) { var normalize = function (x) { return x / 255; }; mix = hsluv.rgbToHsluv(_.map(rgb, normalize)); mix[1] = mix[1] / 100; mix[2] = mix[2] / 100; } else { mix = chroma(rgb).hsl(); } return new tree.Color(mix, alpha, perceptual); }, greyscale: function (color) { if (!('getComponents' in color)) return null; var comp = color.getComponents(); comp.s -= 1; comp.s = clamp(comp.s); return new tree.Color([comp.h, comp.s, comp.l], comp.a, comp.perceptual); }, greyscalep: function (color) { if (!('toPerceptual' in color)) return null; return this.greyscale(color.toPerceptual()); }, '%': function (quoted /* arg, arg, ...*/) { var args = Array.prototype.slice.call(arguments, 1), str = quoted.value; for (var i = 0; i < args.length; i++) { str = str.replace(/%s/, args[i].value) .replace(/%[da]/, args[i].toString()); } str = str.replace(/%%/g, '%'); return new tree.Quoted(str); } }; var image_filter_functors = [ 'emboss', 'blur', 'gray', 'sobel', 'edge-detect', 'x-gradient', 'y-gradient', 'sharpen']; for (var i = 0; i < image_filter_functors.length; i++) { var f = image_filter_functors[i]; tree.functions[f] = (function(f) { return function() { return new tree.ImageFilter(f); }; })(f); } tree.functions['agg-stack-blur'] = function(x, y) { return new tree.ImageFilter('agg-stack-blur', [x, y]); }; tree.functions['scale-hsla'] = function(h0,h1,s0,s1,l0,l1,a0,a1) { return new tree.ImageFilter('scale-hsla', [h0,h1,s0,s1,l0,l1,a0,a1]); }; function number(n) { if (n instanceof tree.Dimension) { return parseFloat(n.unit == '%' ? n.value / 100 : n.value); } else if (typeof(n) === 'number') { return n; } else { return NaN; } } function clamp(val) { return Math.min(1, Math.max(0, val)); } })(require('./tree'));