Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Radar graph #136

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
266 changes: 266 additions & 0 deletions g.radar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
/*
* Radar charts for g.Raphael 2.0
*
* Copyright (c) 2012 Kevin Yank, Avalanche Technology Group
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
*
* Based upon:
* Radar charts for g.Raphael
*
* Copyright (c) 2009 Silvan T. Golega
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
*
* Developed for:
* g.Raphael 2.0 - Charting library, based on Raphaël
*
* Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
*/
(function() {
function Radarchart(paper, cx, cy, r, values, opts) {
opts = opts || {};

var chartinst = this,
arms = [], // holds the values, their positions, paths, arms, circles
chart = paper.set(), // the chart that will be constructed and returned
covers = paper.set(), // holds the areas for event handling
series = paper.set(),
middle_point, // raphael circle for background mesh
mesh = paper.set(), // the background mesh
total = 0,
max = Math.max.apply(Math, values), // the maximum of the values
len = values.length, // number of values
web = {pointarray: [], path: null}; // connecting lines between values

// overwrite default values for options with opts
var default_opts = {
meshwidth: 1,
strokewidth: 2,
stroke: "#f90",
meshcolor: "#999",
helplines: 5,
circleradius: 10,
numbers: true,
numberscolor: "#fff"
};
for (var property in opts)
default_opts[property] = opts[property];
opts = default_opts;
delete default_opts;

// helper function for drawing an arm
var arm = function (sx, sy, r, angle, m) {
var rad = Math.PI / 180,
cos = Math.cos(-angle * rad),
sin = Math.sin(-angle * rad),
x = sx + r * cos,
y = sy + r * sin,
ex = sx + m * cos,
ey = sy + m * sin,
res = {
x: x,
y: y,
//start: {x: sx, y: sy},
//end: {x: ex, y: ey},
path: ["M", cx, cy, "L", x, y].join(','),
rest: ["M", x, y, "L", ex, ey].join(','),
};
return res;
}

// calculate total of all values
for (var i = len; i--;) {
total +=+ values[i];
}

// draw middle point and mesh circles
middle_point = paper.circle(cx, cy, 5).attr({stroke: opts.meshcolor, fill: opts.meshcolor, "stroke-width": opts.meshwidth});
if (opts.helplines){
var helpradius = r / opts.helplines;
for (var i = 0; i < opts.helplines; i++) {
mesh.push(paper.circle(cx, cy, helpradius*(i+1)).attr({stroke: opts.meshcolor, "stroke-width": opts.meshwidth}));
}
}

// calculate the arms
for (var i = 0; i < len; i++) {
arms[i] = arm(cx, cy, r * values[i] / max, i * 360 / len, r);
}

// draw a polygon through the value points
web.pointarray.push("M");
for (var i = 0; i < len; i++) {
web.pointarray.push(arms[i].x, arms[i].y, "L");
}
web.pointarray.push(arms[0].x, arms[0].y);
web.path = paper.path(web.pointarray.join(',')).attr({stroke: opts.stroke, "stroke-width": opts.meshwidth, fill: opts.stroke, "fill-opacity": 0.4});

// draw the value points (and arms) as latest to make sure they are the topmost
for (var i = 0; i < len; i++) {
arms[i].path = paper.path(arms[i].path)
.attr({stroke: opts.stroke, "stroke-width": opts.strokewidth});
arms[i].rest = paper.path(arms[i].rest)
.attr({stroke: opts.meshcolor, "stroke-width": opts.meshwidth});
arms[i].point = paper.circle(arms[i].x, arms[i].y, opts.circleradius)
.attr({stroke: opts.stroke, fill: opts.stroke });
if(opts.numbers){
arms[i].number = paper.text(arms[i].x, arms[i].y+1, i+1).attr(chartinst.txtattr).attr({fill: opts.numberscolor, "text-anchor": "middle"});
}
var cover = paper.set();
cover.push(arms[i].path, arms[i].rest, arms[i].point);
if (arms[i].number)
cover.push(arms[i].number);
covers.push(cover);
series.push(arms[i].point);
}

chart.hover = function (fin, fout) {
fout = fout || function () {};
var that = this;
for (var i = len; i--;) {
(function (arm, cover, j) {
var o = {
arm: arm.point,
number: arm.number,
cover: cover,
cx: cx,
cy: cy,
mx: arm.x,
my: arm.y,
value: values[j],
max: max,
label: that.labels && that.labels[j]
};
o.cover.mouseover(function () {
fin.call(o);
}).mouseout(function () {
fout.call(o);
});
if (o.label){
o.label.mouseover(function () {
fin.call(o);
}).mouseout(function () {
fout.call(o);
});
}
})(arms[i], covers[i], i);
}
return this;
};

// x: where label could be put
// y: where label could be put
// value: value to show
// total: total number to count %
chart.each = function (f) {
if (!Raphael.is(f, "function")) {
return this;
}
var that = this;
for (var i = len; i--;) {
(function (arm, cover, j) {
var o = {
arm: arm.point,
number: arm.number,
cover: cover,
cx: cx,
cy: cy,
x: arm.x,
y: arm.y,
value: values[j],
max: max,
label: that.labels && that.labels[j]
};
f.call(o);
})(arms[i], covers[i], i);
}
return this;
};

chart.click = function (f) {
var that = this;
for (var i = len; i--;) {
(function (arm, cover, j) {
var o = {
arm: arm.point,
number: arm.number,
cover: cover,
cx: cx,
cy: cy,
mx: arm.x,
my: arm.y,
value: values[j],
max: max,
label: that.labels && that.labels[j]
};
cover.click(function () { f.call(o); });
if (o.label){
o.label.click(function () {
f.call(o);
});
}
})(arms[i], covers[i], i);
}
return this;
};

var legend = function (labels, otherslabel, mark, dir) {
var x = cx + r + r / 5,
y = cy,
h = y + 10;

labels = labels || [];
dir = (dir && dir.toLowerCase && dir.toLowerCase()) || "east";
mark = paper[mark && mark.toLowerCase()] || "circle";
chart.labels = paper.set();

for (var i = 0; i < len; i++) {
var clr = series[i].attr("fill"),
txt;

values[i].others && (labels[j] = otherslabel || "Others");
labels[i] = chartinst.labelise(labels[i], values[i], total);
chart.labels.push(paper.set());
chart.labels[i].push(paper[mark](x + 5, h, 8).attr({fill: clr, stroke: "none"}));
chart.labels[i].push(txt = paper.text(x + 20, h, labels[i] || values[i]).attr(chartinst.txtattr).attr({fill: opts.legendcolor || "#000", "text-anchor": "start"}));
if(opts.numbers){
chart.labels[i].push(paper.text(x + 5, h + 1, i + 1).attr(chartinst.txtattr).attr({fill: opts.numberscolor, "text-anchor": "middle"}));
}
covers[i].label = chart.labels[i];
h += txt.getBBox().height * 1.2;
}

var bb = chart.labels.getBBox(),
tr = {
east: [0, -bb.height / 2],
west: [-bb.width - 2 * r - 20, -bb.height / 2],
north: [-r - bb.width / 2, -r - bb.height - 10],
south: [-r - bb.width / 2, r + 10]
}[dir];

chart.labels.translate.apply(chart.labels, tr);
chart.push(chart.labels);
};

if (opts.legend) {
legend(opts.legend, opts.legendothers, opts.legendmark, opts.legendpos);
}

chart.push(series, covers, middle_point, mesh);
chart.series = series;
chart.covers = covers;

return chart;
};

//inheritance
var F = function() {};
F.prototype = Raphael.g;
Radarchart.prototype = new F;

//public
Raphael.fn.radarchart = function(cx, cy, r, values, opts) {
return new Radarchart(this, cx, cy, r, values, opts);
};

})();
33 changes: 33 additions & 0 deletions test/radarchart.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>gRaphaël Static Radar Chart</title>
<link rel="stylesheet" href="css/demo.css" type="text/css" media="screen" charset="utf-8">
<link rel="stylesheet" href="css/demo-print.css" type="text/css" media="print" charset="utf-8">
<script src="../raphael-min.js" type="text/javascript" charset="utf-8"></script>
<script src="../g.raphael.js" type="text/javascript" charset="utf-8"></script>
<script src="../g.radar.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">
window.onload = function () {
var r = Raphael("holder"),
txtattr = {font: "12px 'Fontin Sans', Fontin-Sans, sans-serif"};

r.text(320, 20, "Static Radar Chart").attr(txtattr).attr({"font-size": 20});

static_radar = r.radarchart(
320, 240, 200,
[35, 20, 13, 32, 25, 40, 37, 16, 42],
{circleradius: 8, numbers: false}
);
};
</script>
</head>
<body class="raphael" id="g.raphael.dmitry.baranovskiy.com">
<div id="holder"></div>
<p>
Demo of <a href="http://g.raphaeljs.com/">gRaphaël</a> JavaScript library.
</p>
</body>
</html>
65 changes: 65 additions & 0 deletions test/radarchart2.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>gRaphaël Dynamic Radar Chart</title>
<link rel="stylesheet" href="css/demo.css" type="text/css" media="screen" charset="utf-8">
<link rel="stylesheet" href="css/demo-print.css" type="text/css" media="print" charset="utf-8">
<script src="../raphael-min.js" type="text/javascript" charset="utf-8"></script>
<script src="../g.raphael.js" type="text/javascript" charset="utf-8"></script>
<script src="../g.radar.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">
window.onload = function () {
var r = Raphael("holder"),
txtattr = {font: "12px 'Fontin Sans', Fontin-Sans, sans-serif"};

r.text(320, 20, "Interactive Radar Chart").attr(txtattr).attr({"font-size": 20});

anim_radar = r.radarchart(
320, 240, 100,
[35, 37, 16, 20, 40, 13, 32, 25, 42, 20 ],
{ stroke: "#09f",
helplines: 4,
circleradius: 10,
legend: ["Value number one", "A second value", "And a third one",
"Always give meaningful titles", "Don't you think?", "This legend rocks!"],
href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]
});

anim_radar.hover(function () {
this.arm.stop();
this.arm.attr({r: 12});
this.number.stop();
this.number.attr({"font-size": 20});
if (this.label) {
this.label[0].stop();
this.label[0].attr({r: 10});
this.label[1].attr({"font-weight": "bold"});
this.label[2].stop();
this.label[2].attr({"font-size": 20});
}
}, function () {
this.arm.animate({r: 10}, 500, "bounce");
this.number.animate({"font-size": 12}, 500, "bounce");
if (this.label) {
this.label[0].animate({r: 8}, 500, "bounce");
this.label[1].attr({"font-weight": "normal"});
this.label[2].animate({"font-size": 12}, 500, "bounce");
}
});

anim_radar.click(function () {
this.arm.stop();
window.location.href = "";
});
};
</script>
</head>
<body class="raphael" id="g.raphael.dmitry.baranovskiy.com">
<div id="holder"></div>
<p>
Demo of <a href="http://g.raphaeljs.com/">gRaphaël</a> JavaScript library.
</p>
</body>
</html>