UNPKG

plotter

Version:

Turns an array of data into a graph in a pdf document.

191 lines (165 loc) 5.11 kB
/* Richard Meadows 2012 */ /* -------- Includes -------- */ var exec = require('child_process').exec; var _ = require('underscore'); /* -------- Helper Functions -------- */ /** * Performs a n-point moving average on array. */ function moving_average(array, n) { var nums = []; for (i in array) { if (_.isNumber(array[i])) { nums.push(array[i]); if (nums.length > n) { nums.splice(0,1); } /* Remove the first element of the array */ /* Take the average of the n items in this array */ var sum = _.reduce(nums, function(memo, num){ return memo + num; }, 0); array[i] = sum/nums.length; } } return array; } /** * Performs a n-point maximum on array. */ function moving_maximum(array, n) { var nums = []; for (i in array) { if (_.isNumber(array[i])) { nums.push(array[i]); if (nums.length > n) { nums.splice(0,1); } /* Remove the first element of the array */ /* Take the average of the n items in this array */ var maximum = _.max(nums); array[i] = maximum; } } return array; } /** * Applys an n-point moving filter to a set of series. */ function apply_moving_filter(set, filter, n) { if (!_.isNumber(n)) { n = 3; } for (series in set) { /* For each series */ /* Apply the filter */ set[series] = filter(set[series], n); } return set; } /** * Returns the string to give to gnuplot based on the value of options.time. */ function time_format(options) { if (_.isString(options.time)) { /* Translate the string we've been given into a format */ switch(options.time) { case 'days': case 'Days': return "%d/%m"; case 'hours': case 'Hours': return "%H:%M"; default: /* Presume we've been given a gnuplot-readable time format string */ return options.time; } } else { /* Just default to hours */ return "%H:%M"; } } /** * Sets up gnuplot based on the properties we're given in the options object. */ function setup_gnuplot(gnuplot, options) { /* Setup Gnuplot output to postscript so ps2pdf can interpret it */ gnuplot.stdin.write('set term postscript landscape enhanced color dashed \"Helvetica\" 14\n'); /* Formatting Options */ if (options.time) { gnuplot.stdin.write('set xdata time\n'); gnuplot.stdin.write('set timefmt "%s"\n'); gnuplot.stdin.write('set format x "' + time_format(options.time) + '"\n'); gnuplot.stdin.write('set xlabel "time"\n'); } if (options.title) { gnuplot.stdin.write('set title "'+options.title+'"\n'); } if (options.logscale) { gnuplot.stdin.write('set logscale y\n'); } if (options.xlabel) { gnuplot.stdin.write('set xlabel "'+options.xlabel+'"\n'); } if (options.ylabel) { gnuplot.stdin.write('set ylabel "'+options.ylabel+'"\n'); } /* Setup ticks */ gnuplot.stdin.write('set grid xtics ytics mxtics\n'); gnuplot.stdin.write('set mxtics\n'); /* TODO */ gnuplot.stdin.write('set nokey\n'); } /** * Called after Gnuplot has finished. */ function post_gnuplot_processing(error, stdout, stderr) { /* Print stuff */ console.log('stdout: ' + stdout); console.log('stderr: ' + stderr); if (error !== null) { console.log('exec error: ' + error); } } /* -------- Public Functions -------- */ /** * Plots data to a PDF file. If it does not exist, the PDF file will be created, otherwise this plot will * be appended as a new page. */ function plot(options) { /* Required Options */ if (!options.data || !options.filename) { throw("The options object must have 'data' and 'filename' properties!"); return; } /* Translate data into an object if needs be */ if (_.isArray(options.data)) { if (_.flatten(options.data) == options.data) { /* If it's a one-dimentional array */ options.data = { 'Series 1': options.data }; } } /* Defaults */ if (!options.style) { options.style = 'lines'; /* Default to lines */ } /* Apply moving averages and maximums */ if (options.moving_avg) { options.data = apply_moving_filter(options.data, moving_average, options.moving_avg); } if (options.moving_max) { options.data = apply_moving_filter(options.data, moving_maximum, options.moving_max); } /* Execute Gnuplot specifing a function to be called when it terminates */ gnuplot = exec('gnuplot | ps2pdf - '+options.filename, post_gnuplot_processing); /* Sets up gnuplot based on the properties we've been given in the options object */ setup_gnuplot(gnuplot, options); /* Find out how many series there are */ var series_count = _.values(options.data).length; /* Print the command to actually do the plot */ gnuplot.stdin.write('plot'); var i = 1; while (1) { gnuplot.stdin.write('\'-\' using 1:2 with '+options.style+' lt 1 lc '+(i++)); if (i > series_count) { break; } gnuplot.stdin.write(','); } gnuplot.stdin.write('\n'); /* Print out the data */ for (series in options.data) { /* For each series */ for (key in options.data[series]) { /* For each datapoint */ gnuplot.stdin.write(key+' '+options.data[series][key]+'\n'); } /* Terminate the data */ gnuplot.stdin.write('e\n'); } gnuplot.stdin.end(); } /* -------- Exports -------- */ exports.plot = plot;