1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
|
/*
Tim Redfern Jan 2012 tim@eclectronics.org
Animated graphing - Edmund Rice School Ties
see analytics.js example
http://net.tutsplus.com/tutorials/javascript-ajax/an-introduction-to-the-raphael-js-library/
aim for today: have a few items that can be chosen
1) switch items - multiple lines view .. x amount of lines + 'other'
2) animate between views
3) rollover points
4) label axes
*/
var data,raphael,classOfRange,birthplaces,colleges,locations,fields;
var graphData;
var maxPaths;
window.onload = function() {
loadData();
createMenu();
//alert(document.getElementById("holder").offsetWidth+","+document.getElementById("holder").offsetHeight);
//seems predictable, gives same result in chrome, firefox, mobile safari
var graph=document.getElementById('graph');
raphael = new Raphael(graph, graph.offsetWidth, graph.offsetHeight);
drawGraph();
}
function graphableData(name) {
//organises a range of data to graph against a variable value
this.name=name;
this.data={};
this.max=0;
this.addElement = function(element,value) {
if (element in this.data) {
if (value in this.data[element]) this.data[element][value]++;
else this.data[element][value]=1;
}
else {
this.data[element]={};
this.data[element][value]=1
}
if (this.data[element][value]>this.max) this.max=this.data[element][value];
}
this.getElement = function(element) {
if (element in this.data) return data[element]; //returns an associative array
}
this.summary = function() {
var s= ""+name+": "+Object.keys(this.data).length+" elements, max "+this.max+"\n";
var k=Object.keys(this.data);
//s+=k+":";
for (i in k) {
s+=k[i]+":";
var l=Object.keys(this.data[k[i]]);
for (j in l) s+=l[j]+"="+this.data[k[i]][l[j]]+",";
s+="\n";
}
return s+"\n";
}
}
function loadData() {
maxPaths=8;
//replace with json loader
fields=["Class size","Location","Birthplace","College","Career"];
fieldNames=["classSize","location","birthplace","college","career"];
//basic { name:"Joe", surname:"Murphy",school:"Dublin",classOf:"86",location:"Ireland",email:"joem20156@gmail.com"},
//extended { name:"Joe", surname:"Murphy",school:"Dublin",classOf:"86",location:"Ireland",email:"joem20156@gmail.com",birthplace:"Ireland",college:"UCD",career:"construction",sports:["soccer","hurling"],nickname:"Virgil"},
data = [
{ school:"Dublin",classOf:"86",location:"Ireland",birthplace:"Ireland",college:"UCD",career:"construction",sports:["soccer","hurling"],nickname:"Virgil"},
{ school:"Dublin",classOf:"87",location:"Ireland",birthplace:"Ireland",college:"NUIG",career:"law",sports:["soccer","gaelic"],nickname:"Con"},
{ school:"Dublin",classOf:"86",location:"Ireland",birthplace:"Ireland",college:"TCD",career:"teaching",sports:["gaelic","hurling"],nickname:"Morty"},
{ school:"Dublin",classOf:"88",location:"USA",birthplace:"UK",college:"UL",career:"army",sports:["soccer"],nickname:"Mick"},
{ school:"Dublin",classOf:"87",location:"Ireland",birthplace:"Ireland",college:"UCD",career:"law",sports:["soccer","gaelic"],nickname:"Johnjoe"},
{ school:"Dublin",classOf:"86",location:"UK",birthplace:"Ireland",college:"TCD",career:"teaching",sports:["handball","hurling"],nickname:"Mossy"},
{ school:"Dublin",classOf:"89",location:"USA",birthplace:"UK",college:"UL",career:"army",sports:["soccer"],nickname:"Mick"},
{ school:"Dublin",classOf:"90",location:"Ireland",birthplace:"Ireland",college:"UCD",career:"law",sports:["soccer","gaelic"],nickname:"Johnjoe"},
{ school:"Dublin",classOf:"88",location:"Ireland",birthplace:"Ireland",college:"UCC",career:"teaching",sports:["handball","hurling"],nickname:"Mossy"},
];
//analyse data to populate menus etc, this may be loaded direct from corresponding server data
classOfRange={};
classOfRange.start=3000;
classOfRange.end=0;
birthplaces={};
colleges={};
locations={};
for (var i in data) {
var year = parseInt(data[i].classOf);
classOfRange.start=Math.min(classOfRange.start,year);
classOfRange.end=Math.max(classOfRange.end,year);
if (data[i].birthplace in birthplaces) birthplaces[data[i].birthplace]++;
else birthplaces[data[i].birthplace]=1;
if (data[i].college in colleges) colleges[data[i].college]++;
else colleges[data[i].college]=1;
if (data[i].location in locations) locations[data[i].location]++;
else locations[data[i].location]=1;
}
var numYears=(classOfRange.end-classOfRange.start);
//prepare all data for graphing
graphData={};
for (i in fields) graphData[fields[i]]=new graphableData(fields[i]);
for (var i in data) {
var yearField
for (j in fields) {
if (fields[j]=="Class size") {
graphData[fields[j]].addElement("Class size",parseInt(data[i].classOf));
}
else graphData[fields[j]].addElement(data[i][fieldNames[j]],parseInt(data[i].classOf));
}
}
///* test
var message="from "+classOfRange.start+" to "+classOfRange.end+"\n";
for (var i in graphData) message+=graphData[i].summary();
alert(message);
//*/
}
function drawGraph() {
var color = "hsl(" + [.6, .5, .5] + ")";
var numYears=(classOfRange.end-classOfRange.start);
var gutterX=10,
gutterY=16;
//draw grid
var gridX=(raphael.width-(gutterX*2))/numYears;
var gridY=(raphael.height-(gutterY*2));
raphael.drawGrid(gutterX, gutterY, raphael.width-(gutterX*2), raphael.height -(gutterY*2), numYears, 10, "#000");
//label axis
var txt = {font: '12px Helvetica, Arial', fill: "#fff"};
for (var i=0;i<=numYears;i++) raphael.text(gutterX+(i*gridX),raphael.height-gutterY+8,classOfRange.start+i).attr(txt).toBack();
//construct graph
var graph = raphael.path().attr({stroke: color, "stroke-width": 4, "stroke-linejoin": "round"}),
bgGraph = raphael.path().attr({stroke: "none", opacity: .3, fill: color});
//default graph is 'Class size'
//find largest class
var classSize=[];
for (var i=0;i<=numYears;i++) classSize[i]=0;
for (var i in data) {
classSize[parseInt(data[i].classOf)-classOfRange.start]++;
}
var classMax=0;
for (var i in classSize) classMax=Math.max(classMax,classSize[i]);
var path,bgPath;
var stepY=(gridY/classMax);
for (var i=0;i<=numYears;i++) {
var x=gutterX+(i*gridX),
y=raphael.height-gutterY-(stepY*classSize[i]);
if (!i) {
path=["M",x,y,"C",x,y];
bgPath=["M", gutterX , raphael.height - gutterY, "L", x, y, "C", x, y];
}
if (i&&i<numYears-1) {
var Y0 = Math.round(raphael.height - gutterY - (stepY*classSize[i-1])),
X0 = Math.round(gutterX + (gridX * (i - .5))),
Y2 = Math.round(raphael.height - gutterY - (stepY*classSize[i+1])),
X2 = Math.round(gutterX + (gridX * (i + 1.5)));
var a = getAnchors(X0, Y0, x, y, X2, Y2);
path = path.concat([a.x1, a.y1, x, y, a.x2, a.y2]);
bgPath = bgPath.concat([a.x1, a.y1, x, y, a.x2, a.y2]);
}
}
path = path.concat([x, y, x, y]);
bgPath = bgPath.concat([x, y, x, y, "L", x, raphael.height - gutterY, "z"]);
graph.attr({path: path});
bgGraph.attr({path: bgPath});
}
function createMenu() {
var menu="<select id='select' onchange='doGraph(this.selectedIndex)'>\n";
for (i in fields) menu+="<option>"+fields[i]+"</option>/n";
menu+="</select>\n";
document.getElementById("menus").innerHTML=menu;
}
function doGraph(which){
//draw 1 or multiple graphs. animate between items
//maximum number of graphs
alert(fields[which]);
}
|