File: lib/graph.js
/*
* niViz -- snow profiles visualization
* Copyright (C) 2015 WSL/SLF - Fluelastrasse 11 - 7260 Davos Dorf - Switzerland.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
(function (niviz) {
'use strict';
// --- Module Dependencies ---
var EventEmitter = niviz.EventEmitter;
var synchronizable = niviz.synchronizable;
var util = niviz.util;
var inherits = util.inherits;
var fonts = niviz.SVGFonts;
var emitter = new EventEmitter();
/** @module niviz */
/**
* Snowpack profile graph.
*
* @class Graph
* @constructor
*
* @extends EventEmitter
* @extends Synchronizable
*/
function Graph() {
// EventEmitter.call(emitter);
synchronizable(this);
this.emitter = emitter;
}
inherits(Graph, EventEmitter);
/**
* Registry of graph implementations.
*
* @property
* @type Object
* @static
*/
Graph.type = {};
/**
* Makes the passed-in constructor inherit from
* Graph and registers it for the given type
*
* @method implement
* @static
*
* @param {Function} C The constructor.
* @param {String} name The name of the graph (unique).
* @param {String} [type] The type, e. g. 'profile' or 'timeline'.
* @param {String} [base] The graph from which to inherit.
*
* @returns {Function} The extended constructor.
*/
Graph.implement = function (C, name, type, base) {
inherits(C, base && Graph.type[base] || Graph);
Graph.type[name] = C;
Graph.type[name].genus = type;
return C;
};
/**
* Request to draw a graph of a certain type. Check whether type is
* registered and call the constructor upon the graph.
*
* @method draw
* @static
*
* @param {String} name The name of the graph (unique).
* @param {Station} station
* @param {Object} canvas A svg element that will be used as SnapSVG paper
* @param {Object} properties Properties to be passed to the graph
*
* @returns {Graph} An instance of the graph type
*/
Graph.draw = function (type, station, canvas, properties) {
var G = Graph.type[type];
if (!G) throw new Error('no graph of type ' + type + ' found');
if ((station.profiles && station.profiles[0]) || station.data) {
return (new G(station, canvas, properties));
}
};
/**
* @method draw
* @abstract
*/
Graph.prototype.draw = function () {
throw new Error('not implemented');
};
/**
* Convert a graph into JSON or SVG format.
*
* @method convert
* @static
*
* @param {String} type SVG or JSON
* @param {Graph} object
*
* @returns {Object}
*/
Graph.convert = function (type, object, callback) {
if (type === 'SVG') {
if (typeof object.paper.toString === 'function') { //Snap.svg
var paper = object.paper.clone();
var removableElements = paper.selectAll('[class=removable]');
removableElements.items && removableElements.items.forEach(function (n) {
n.remove();
});
paper.attr({viewBox: '0 0 '
+ Math.round(object.canvas.width()) + ' '
+ Math.round(object.canvas.height())});
var str = paper.toString();
str = str.replace('<defs>', fonts);
str = str.replace('Created with Snap', 'Created with niViz on '
+ moment().format('YYYY-MM-DDThh:mm:ss'));
str = str.replace(' spinner="processing"', '');
str = str.replace(' class="body"', '');
str = str.replace(/(<svg [^>]*)(style="[^"]*" )([^>]*)/, '$1$3');
str = str.replace(/(<svg [^>]*)(width="[^"]*" )([^>]*)/, '$1$3');
str = str.replace(/(<svg [^>]*)(height="[^"]*" )([^>]*)/, '$1$3');
str = str.replace(/(<svg [^>]*)(id="[^"]*" )([^>]*)/, '$1$3');
str = str.replace(/<svg /, '<svg height="100%" width="100%" ');
str = str.replace('xmlns="http://www.w3.org/2000/svg"',
'xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"');
callback(str);
paper.remove();
} else {
throw new Error('graph.js SVG rendering error: is the graph using SnapSVG?');
}
} else if (type === 'PNG') {
if (typeof object.paper.toDataURL === 'function') { //Snap.svg
var c = window.document.createElement('canvas');
var att1 = document.createAttribute('id');
att1.value = 'expcanvas';
var paper = object.paper.clone();
var removableElements = paper.selectAll('[class=removable]');
removableElements.items && removableElements.items.forEach(function (n) {
n.remove();
});
var svg = paper.toString();
// To make PNG export work in Firefox:
svg = svg.replace('xmlns="http://www.w3.org/2000/svg"',
'xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"');
canvg(c, svg, {renderCallback: function (result) {
/* Draw PNG on a white background if desired */
var ctx = c.getContext('2d');
if (niviz.Common.defaults.PNG_background === 'white') {
ctx.globalCompositeOperation = 'destination-over';
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, c.width, c.height);
}
var dataURL = c.toDataURL('image/png');
var bin = window.atob(dataURL.replace('data:image/png;base64,', ''));
var ia = new Uint8Array(bin.length);
for (var i = 0; i < bin.length; i++) {
ia[i] = bin.charCodeAt(i);
}
callback(ia);
paper.remove();
}});
}
} else if (type === 'JSON') {
if (object.profile) { // Download current profile only
var station = new niviz.Station();
station.name = object.station.name;
station.id = object.station.id;
station.position = object.station.position;
station.observer = object.station.observer;
station.push(object.profile);
callback(JSON.stringify(station));
return;
}
callback(JSON.stringify(object.data));
} else if (type === 'CSV') {
if (niviz.Meteograph.prototype.isPrototypeOf(object))
callback(niviz.CSV.serialize(object));
else
throw new Error('CSV export only defined for Meteograph objects');
} else if (type === 'CAAML5') {
if (object.station && object.station.length && object.profile)
callback(niviz.CAAML5.serialize(object));
else
throw new Error('CAAML5 export only defined for graphs of profiles');
} else if (type === 'CAAML') {
if (object.station && object.station.length && object.profile)
callback(niviz.CAAML.serialize(object));
else
throw new Error('CAAML export only defined for graphs of profiles');
}
};
// --- Module Exports ---
niviz.Graph = Graph;
niviz.draw = Graph.draw;
niviz.convert = Graph.convert;
}(niviz));