PDA

View Full Version : SVG in maps and graphs


limo
06-05-2007, 09:44
Hi to all,
I already opened this some time ago but I think now it is time to realize this relative easily.

SVG is pure xml, so easy to make templates with macros (see example).

So creating svg map from php is really easy. Get template from DB, expand macros and embed it into html. Visualisation is on client so server does not need to hard work with bitmaps.

This could be maybe totaly external thing to zabbix. It is enaught to use zabbix php function to expand macros.

What do you thing ?

Nice with this design is, that there are no limits in map. You can make box as big as some value. Or you can do some gauge showing percents. Everything is in xml code.


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
<svg
id="svg602"
sodipodi:version="0.34"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="210mm"
height="297mm"
xml:space="preserve"
sodipodi:docbase="/tmp"
sodipodi:docname="/tmp/test.svg"><defs
id="defs604" /><sodipodi:namedview
id="base" /><rect
style="font-size:12;fill:#808080;fill-rule:evenodd;stroke-width:1;"
id="rect605"
width="123.404022"
height="164.538742"
x="106.264587"
y="239.952286" /><rect
style="font-size:12;fill:#808080;fill-rule:evenodd;stroke-width:1;"
id="rect606"
width="113.120392"
height="195.389709"
x="356.500549"
y="555.318176" /><rect
style="font-size:12;fill:#808080;fill-rule:evenodd;stroke-width:1;"
id="rect607"
width="75.4136047"
height="78.8414001"
x="503.899811"
y="137.115601" /><text
style="fill:black;stroke:none;font-family:helvetica;font-style:normal;font-weight:normal;font-size:12px;fill-opacity:1;stroke-opacity:1;stroke-width:1pt;stroke-linejoin:miter;stroke-linecap:butt;"
x="123.404037"
y="435.34198"
id="text608"><tspan
id="tspan609">host1 - {host1:item.last(0)}</tspan></text><text
style="fill:black;stroke:none;font-family:helvetica;font-style:normal;font-weight:normal;font-size:12px;fill-opacity:1;stroke-opacity:1;stroke-width:1pt;stroke-linejoin:miter;stroke-linecap:butt;"
x="534.750854"
y="246.80806"
id="text611"><tspan
id="tspan612">host2 - {host2:item2.last(1)}</tspan></text><text
style="fill:black;stroke:none;font-family:helvetica;font-style:normal;font-weight:normal;font-size:12px;fill-opacity:1;stroke-opacity:1;stroke-width:1pt;stroke-linejoin:miter;stroke-linecap:butt;"
x="387.351562"
y="774.703064"
id="text614"><tspan
id="tspan615">host3 - {host3:item2.last(1)}</tspan></text></svg>

Alexei
06-05-2007, 09:49
Do Firefox and MS Explorer support SVG out-of-the box?

limo
06-05-2007, 22:40
Yes and no :)

Mozilla SHOULD support native svg in windows and linux. Implementation is not full but it is really enaught for use with zabbix.

IE6 and IE7 has support with adobe svg plugin (it SHOULD be automaticaly instaled when you install acrobat reader)

Yes, support of svg can be limiting factor now. But I think most of admins has either mozilla or acrobat reader instaled :)

Alexei
07-05-2007, 08:46
Mozilla I use (1.5.0.11) does not support SVG.

I keep SVG in mind, I just cannot make a decision of switching everything to SVG due to a limited support of SVG.

limo
07-05-2007, 10:00
As I said, SVG is supported in mozilla. 1.5 has native support already. It is missing only in 1.0.x versions. But you cannot look to pages which are made for svg plugin implementation from Adobe.. It looks like unsopported.

Look here: http://developer.mozilla.org/en/docs/SVG_in_Firefox_1.5

And here: http://www.croczilla.com/svg/samples/

and you should see svg in action..

Alexei
07-05-2007, 10:54
Indeed it works now. I must have been used wrong SVG samples.

Vince2
07-05-2007, 14:22
SVG could also be used for graphs :
http://www.polarcloud.com/tomato/
http://m0n0.ch/wall/images/screens/status_graph.png

quekky
12-05-2007, 06:55
agree on using SVG. I use pfsense (clone of mono) and it uses realtime SVG with some very simple scripting


<?xml version="1.0" encoding="iso-8859-1"?>
<svg width="100%" height="100%" viewBox="0 0 200 100" preserveAspectRatio="none" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" onload="init(evt)">
<g id="graph">
.....
</g>
<script type="text/ecmascript">
<![CDATA[

/**
* getURL is a proprietary Adobe function, but it's simplicity has made it very
* popular. If getURL is undefined we spin our own by wrapping XMLHttpRequest.
*/
if (typeof getURL == 'undefined') {
getURL = function(url, callback) {
if (!url)
throw 'No URL for getURL';

try {
if (typeof callback.operationComplete == 'function')
callback = callback.operationComplete;
} catch (e) {}
if (typeof callback != 'function')
throw 'No callback function for getURL';

var http_request = null;
if (typeof XMLHttpRequest != 'undefined') {
http_request = new XMLHttpRequest();
}
else if (typeof ActiveXObject != 'undefined') {
try {
http_request = new ActiveXObject('Msxml2.XMLHTTP');
} catch (e) {
try {
http_request = new ActiveXObject('Microsoft.XMLHTTP');
} catch (e) {}
}
}
if (!http_request)
throw 'Both getURL and XMLHttpRequest are undefined';

http_request.onreadystatechange = function() {
if (http_request.readyState == 4) {
callback( { success : true,
content : http_request.responseText,
contentType : http_request.getResponseHeader("Content-Type") } );
}
}
http_request.open('GET', url, true);
http_request.send(null);
}
}

var SVGDoc = null;
var last_ifin = 0;
var last_ifout = 0;
var last_ugmt = 0;
var max = 0;
var plot_in = new Array();
var plot_out = new Array();

var max_num_points = 120; // maximum number of plot data points
var step = 200 / max_num_points ;
var unit = 'bits';
var scale_type = 'up';

function init(evt) {
SVGDoc = evt.target.ownerDocument;
SVGDoc.getElementById("switch_unit").addEventListener("mousedown", switch_unit, false);
SVGDoc.getElementById("switch_scale").addEventListener("mousedown", switch_scale, false);

fetch_data();
}

function switch_unit(event)
{
SVGDoc.getElementById('switch_unit').firstChild.da ta = 'Switch to ' + unit + '/s';
unit = (unit == 'bits') ? 'bytes' : 'bits';
}

function switch_scale(event)
{
scale_type = (scale_type == 'up') ? 'follow' : 'up';
SVGDoc.getElementById('switch_scale').firstChild.d ata = 'AutoScale (' + scale_type + ')';
}

function fetch_data() {
getURL('ifstats.php?if=hme0', plot_data);
}

function plot_data(obj) {
// Show datetimelegend
var now = new Date();
var datetime = (now.getMonth()+1) + "/" + now.getDate() + "/" + now.getFullYear() + ' ' +
LZ(now.getHours()) + ":" + LZ(now.getMinutes()) + ":" + LZ(now.getSeconds());
SVGDoc.getElementById('datetime').firstChild.data = datetime;

if (!obj.success)
return handle_error(); // getURL failed to get data

var t = obj.content.split("|");
var ugmt = parseFloat(t[0]); // ugmt is an unixtimestamp style
var ifin = parseInt(t[1]); // number of bytes received by the interface
var ifout = parseInt(t[2]); // number of bytes sent by the interface
var scale;

if (!isNumber(ifin) || !isNumber(ifout))
return handle_error();

var diff_ugmt = ugmt - last_ugmt;
var diff_ifin = ifin - last_ifin;
var diff_ifout = ifout - last_ifout;

if (diff_ugmt == 0)
diff_ugmt = 1; /* avoid division by zero */

last_ugmt = ugmt;
last_ifin = ifin;
last_ifout = ifout;

switch (plot_in.length) {
case 0:
SVGDoc.getElementById("collect_initial").setAttributeNS(null, 'visibility', 'visible');
plot_in[0] = diff_ifin / diff_ugmt;
plot_out[0] = diff_ifout / diff_ugmt;
setTimeout('fetch_data()',1000);
return;
case 1:
SVGDoc.getElementById("collect_initial").setAttributeNS(null, 'visibility', 'hidden');
break;
case max_num_points:
// shift plot to left if the maximum number of plot points has been reached
var i = 0;
while (i < max_num_points) {
plot_in[i] = plot_in[i+1];
plot_out[i] = plot_out[++i];
}
plot_in.length--;
plot_out.length--;
}

plot_in[plot_in.length] = diff_ifin / diff_ugmt;
plot_out[plot_out.length]= diff_ifout / diff_ugmt;
var index_plot = plot_in.length - 1;

SVGDoc.getElementById('graph_in_txt').firstChild.d ata = formatSpeed(plot_in[index_plot], unit);
SVGDoc.getElementById('graph_out_txt').firstChild. data = formatSpeed(plot_out[index_plot], unit);

/* determine peak for sensible scaling */
if (scale_type == 'up') {
if (plot_in[index_plot] > max)
max = plot_in[index_plot];
if (plot_out[index_plot] > max)
max = plot_out[index_plot];
}
else if (scale_type == 'follow') {
i = 0;
max = 0;
while (i < plot_in.length) {
if (plot_in[i] > max)
max = plot_in[i];
if (plot_out[i] > max)
max = plot_out[i];
i++;
}
}

var rmax; // max, rounded up

if (unit == 'bits') {
/* round up max, such that
100 kbps -> 200 kbps -> 400 kbps -> 800 kbps -> 1 Mbps -> 2 Mbps -> ... */
rmax = 12500;
i = 0;
while (max > rmax) {
i++;
if (i && (i % 4 == 0))
rmax *= 1.25;
else
rmax *= 2;
}
} else {
/* round up max, such that
10 KB/s -> 20 KB/s -> 40 KB/s -> 80 KB/s -> 100 KB/s -> 200 KB/s -> 400 KB/s -> 800 KB/s -> 1 MB/s ... */
rmax = 10240;
i = 0;
while (max > rmax) {
i++;
if (i && (i % 4 == 0))
rmax *= 1.25;
else
rmax *= 2;

if (i == 8)
rmax *= 1.024;
}
}

scale = 100 / rmax;

/* change labels accordingly */
SVGDoc.getElementById('grid_txt1').firstChild.data = formatSpeed(3*rmax/4,unit);
SVGDoc.getElementById('grid_txt2').firstChild.data = formatSpeed(2*rmax/4,unit);
SVGDoc.getElementById('grid_txt3').firstChild.data = formatSpeed(rmax/4,unit);

var path_in = "M 0 " + (100 - (plot_in[0] * scale));
var path_out = "M 0 " + (100 - (plot_out[0] * scale));
for (i = 1; i < plot_in.length; i++)
{
var x = step * i;
var y_in = 100 - (plot_in[i] * scale);
var y_out = 100 - (plot_out[i] * scale);
path_in += " L" + x + " " + y_in;
path_out += " L" + x + " " + y_out;
}

SVGDoc.getElementById('error').setAttributeNS(null , 'visibility', 'hidden');
SVGDoc.getElementById('graph_in').setAttributeNS(n ull, 'd', path_in);
SVGDoc.getElementById('graph_out').setAttributeNS( null, 'd', path_out);

setTimeout('fetch_data()',1000);
}

function handle_error() {
SVGDoc.getElementById("error").setAttributeNS(null, 'visibility', 'visible');
setTimeout('fetch_data()',1000);
}

function isNumber(a) {
return typeof a == 'number' && isFinite(a);
}

function formatSpeed(speed, unit) {
if (unit == 'bits')
return formatSpeedBits(speed);
if (unit == 'bytes')
return formatSpeedBytes(speed);
}

function formatSpeedBits(speed) {
// format speed in bits/sec, input: bytes/sec
if (speed < 125000)
return Math.round(speed / 125) + " Kbps";
if (speed < 125000000)
return Math.round(speed / 1250)/100 + " Mbps";
// else
return Math.round(speed / 1250000)/100 + " Gbps"; /* wow! */
}

function formatSpeedBytes(speed) {
// format speed in bytes/sec, input: bytes/sec
if (speed < 1048576)
return Math.round(speed / 10.24)/100 + " KB/s";
if (speed < 1073741824)
return Math.round(speed / 10485.76)/100 + " MB/s";
// else
return Math.round(speed / 10737418.24)/100 + " GB/s"; /* wow! */
}

function LZ(x) {
return (x < 0 || x > 9 ? "" : "0") + x;
}

]]>
</script>
</svg>