Skip to content

Commit

Permalink
Updates for v1.2.0 release.
Browse files Browse the repository at this point in the history
  • Loading branch information
jheer committed Apr 11, 2013
1 parent fce2e17 commit 2c4dece
Show file tree
Hide file tree
Showing 12 changed files with 224 additions and 199 deletions.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ vega.js: \
src/core/View.js \
src/core/Spec.js \
src/headless/_package.js \
src/headless/HeadlessView.js \
src/headless/View.js \
src/headless/render.js \
src/core/_end.js

%.min.js: %.js Makefile
Expand Down
42 changes: 39 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
Vega: A Visualization Grammar
====

**Vega** is a _visualization grammar_, a declarative format for creating and
saving visualization designs. With Vega you can describe data visualizations in a JSON format, and generate interactive views using either HTML5 Canvas or SVG.
**Vega** is a _visualization grammar_, a declarative format for creating and
saving visualization designs. With Vega you can describe data visualizations
in a JSON format, and generate interactive views using either HTML5 Canvas or
SVG.

To learn more, [visit the wiki](https://github.com/trifacta/vega/wiki).

Expand Down Expand Up @@ -30,4 +32,38 @@ The JavaScript build process depends on supporting node.js modules. First,
make sure you have node.js and npm (Node Package Manager) installed and
accessible from the command line. Run `make install` to install these modules
into a local node_modules folder. The make install command will create the
node_modules folder if it does not exist.
node_modules folder if it does not exist.

## Vega Server-Side and Command Line Tools

Vega can also be run server-side using node.js. When running in "headless"
mode, Vega can be used to render specifications directly to PNG or SVG.

In addition to the summary below, [see the Headless Mode wiki
documentation](https://github.com/trifacta/vega/wiki/Headless-Mode) for more
information.

### Command Line Tools

Vega includes two command line tools for converting Vega JSON specifications
to rendered PNG or SVG:

* __vg2png__: `vg2png [-b basedir] vega_json_file [output_png_file]`
* __vg2svg__: `vg2svg [-b basedir] [-h] vega_json_file [output_svg_file]`

Within the Vega project directories, you can invoke these utilities using
`./bin/vg2png` or `./bin/vg2svg`. If you import Vega using npm, these commands
are accessible either locally (`./node_modules/bin/vg2png`) or globally
(`vg2png`) depending on how you install the Vega package.

### Using Vega in node.js Projects

To include Vega in a node project, first install it from the command line
using npm (`npm install vega`) or by including `"vega"` (version 1.2.0 or
higher) among the dependencies in your package.json file. Then include Vega in
your node.js JavaScript code using `require("vega")`.

When running in node.js, Vega can use a "headless" rendering mode for
generating visualizations outside the browser. Internally, Vega uses a custom
view class (`vg.headless.View`) for headless rendering. However, most
applications can simply use the convenience method `vg.headless.render`.
15 changes: 9 additions & 6 deletions bin/vg2png.js → bin/vg2png
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ var specFile = args._[0],
fs.readFile(specFile, "utf8", function(err, text) {
if (err) throw err;
var spec = JSON.parse(text);
convert(spec);
render(spec);
});

// ---
Expand All @@ -51,9 +51,12 @@ function writePNG(canvas, file) {
stream.on("data", function(chunk) { out.write(chunk); });
}

function convert(spec) {
vg.headless.convert(spec, "canvas", function(err, data) {
if (err) throw err;
writePNG(data.canvas, outputFile);
});
function render(spec) {
vg.headless.render(
{spec: spec, renderer: "canvas"},
function(err, data) {
if (err) throw err;
writePNG(data.canvas, outputFile);
}
);
}
15 changes: 9 additions & 6 deletions bin/vg2svg.js → bin/vg2svg
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ var header = args.h ? svgHeader : "",
fs.readFile(specFile, "utf8", function(err, text) {
if (err) throw err;
var spec = JSON.parse(text);
convert(spec);
render(spec);
});

// ---
Expand All @@ -62,9 +62,12 @@ function writeSVG(svg, file) {
}
}

function convert(spec) {
vg.headless.convert(spec, "svg", function(err, data) {
if (err) throw err;
writeSVG(data.svg, outputFile);
});
function render(spec) {
vg.headless.render(
{spec: spec, renderer:"svg"},
function(err, data) {
if (err) throw err;
writeSVG(data.svg, outputFile);
}
);
}
9 changes: 5 additions & 4 deletions examples/lib/d3.v3.min.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
},
"main": "index.js",
"bin": {
"vg2png": "./bin/vg2png.js",
"vg2svg": "./bin/vg2svg.js"
"vg2png": "./bin/vg2png",
"vg2svg": "./bin/vg2svg"
},
"dependencies": {
"d3": "3.1.4",
Expand Down
5 changes: 5 additions & 0 deletions src/_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ vg.config.isNode = typeof exports !== 'undefined' && this.exports !== exports;
// used only for server-side operation
vg.config.baseURL = "";

// version and namepsaces for exported svg
vg.config.svgNamespace =
'version="1.1" xmlns="http://www.w3.org/2000/svg" ' +
'xmlns:xlink="http://www.w3.org/1999/xlink"';

// default axis properties
vg.config.axis = {
ticks: 10,
Expand Down
55 changes: 48 additions & 7 deletions src/headless/HeadlessView.js → src/headless/View.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
vg.HeadlessView = (function() {
vg.headless.View = (function() {

var view = function(width, height, pad, type) {
this._canvas = null;
this._type = type;
this._el = "body";
this._build = false;
this._model = new vg.Model();
this._width = width || 500;
Expand All @@ -14,6 +15,15 @@ vg.HeadlessView = (function() {

var prototype = view.prototype;

prototype.el = function(el) {
if (!arguments.length) return this._el;
if (this._el !== el) {
this._el = el;
this.initialize();
}
return this;
};

prototype.width = function(width) {
if (!arguments.length) return this._width;
if (this._width !== width) {
Expand Down Expand Up @@ -66,6 +76,40 @@ vg.HeadlessView = (function() {
prototype.canvas = function() {
return this._canvas;
};

prototype.canvasAsync = function(callback) {
var r = this._renderer;

function wait() {
if (r.pendingImages() === 0) {
this.render(); // re-render with all images
callback(this._canvas);
} else {
setTimeout(wait, 10);
}
}

// if images loading, poll until ready
(r.pendingImages() > 0) ? wait() : callback(this._canvas);
};

prototype.svg = function() {
if (this._type !== "svg") return null;

var p = this._padding,
w = this._width + (p ? p.left + p.right : 0),
h = this._height + (p ? p.top + p.bottom : 0);

// build svg text
var svg = d3.select(this._el)
.select("svg").node().innerHTML
.replace(/ href=/g, " xlink:href="); // ns hack. sigh.

return '<svg '
+ 'width="' + w + '" '
+ 'height="' + h + '" '
+ vg.config.svgNamespace + '>' + svg + '</svg>'
};

prototype.initialize = function() {
var w = this._width,
Expand Down Expand Up @@ -100,11 +144,8 @@ vg.HeadlessView = (function() {
var tw = w + pad.left + pad.right,
th = h + pad.top + pad.bottom;

// the "dom" element
var el = "body";

// configure renderer
this._renderer.initialize(el, w, h, pad);
this._renderer.initialize(this._el, w, h, pad);
}

prototype.render = function(items) {
Expand All @@ -127,13 +168,13 @@ vg.HeadlessView = (function() {
// headless view constructor factory
// takes definitions from parsed specification as input
// returns a view constructor
vg.HeadlessView.Factory = function(defs) {
vg.headless.View.Factory = function(defs) {
return function(opt) {
var w = defs.width,
h = defs.height,
p = defs.padding,
r = opt.renderer || "canvas",
v = new vg.HeadlessView(w, h, p, r).defs(defs);
v = new vg.headless.View(w, h, p, r).defs(defs);
if (defs.data.load) v.data(defs.data.load);
if (opt.data) v.data(opt.data);
return v;
Expand Down
82 changes: 1 addition & 81 deletions src/headless/_package.js
Original file line number Diff line number Diff line change
@@ -1,81 +1 @@
vg.headless = (function() {

var svgNS = 'version="1.1" xmlns="http://www.w3.org/2000/svg" ' +
'xmlns:xlink="http://www.w3.org/1999/xlink"';

function extractSVG(view) {
var p = view.padding(),
w = view.width() + (p ? p.left + p.right : 0),
h = view.height() + (p ? p.top + p.bottom : 0),
svg = "";

// build svg text
d3.selectAll("svg").each(function() {
svg = this.innerHTML + svg;
});
svg = svg.replace(/ href=/g, " xlink:href="); // requires a hack. sigh.

return {
svg : '<svg '
+ 'width="' + w + '" '
+ 'height="' + h + '" '
+ svgNS + '>' + svg + '</svg>'
};
}

function extractCanvas(view) {
return {canvas: view.canvas()};
}

function render(opt, callback) {
function draw(chart) {
try {
// create and render view
var view = chart({
data: opt.data,
renderer: opt.renderer
}).update();

if (opt.renderer === "svg") {
// extract rendered svg
callback(null, extractSVG(view));
} else {
// extract rendered canvas
var r = view.renderer();
if (r.pendingImages() === 0) {
// if no images loading, return now
callback(null, extractCanvas(view));
} else {
// if images loading, poll until ready
function wait() {
if (r.pendingImages() === 0) {
view.render(); // re-render with all images
callback(null, extractCanvas(view));
} else setTimeout(wait, 10);
}
wait();
}
}
} catch (err) {
callback(err, null);
}
}
vg.parse.spec(opt.spec, draw, vg.HeadlessView.Factory);
}

function convert(spec, type, callback) {
type = type || "canvas";
var opt = {
renderer: type,
spec: spec,
data: null
};
render(opt, callback);
}

return {
convert: convert,
render: render
};

})();
vg.headless = {};
25 changes: 25 additions & 0 deletions src/headless/render.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
vg.headless.render = function(opt, callback) {
function draw(chart) {
try {
// create and render view
var view = chart({
data: opt.data,
renderer: opt.renderer
}).update();

if (opt.renderer === "svg") {
// extract rendered svg
callback(null, {svg: view.svg()});
} else {
// extract rendered canvas, waiting for any images to load
view.canvasAsync(function(canvas) {
callback(null, {canvas: canvas});
});
}
} catch (err) {
callback(err, null);
}
}

vg.parse.spec(opt.spec, draw, vg.headless.View.Factory);
};
Loading

0 comments on commit 2c4dece

Please sign in to comment.