0
|
1 /*
|
|
2 * PieChart.js
|
|
3 *
|
|
4 * Copyright (c) 2013, Sebastian Kruse. All rights reserved.
|
|
5 *
|
|
6 * This library is free software; you can redistribute it and/or
|
|
7 * modify it under the terms of the GNU Lesser General Public
|
|
8 * License as published by the Free Software Foundation; either
|
|
9 * version 3 of the License, or (at your option) any later version.
|
|
10 *
|
|
11 * This library is distributed in the hope that it will be useful,
|
|
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
14 * Lesser General Public License for more details.
|
|
15 *
|
|
16 * You should have received a copy of the GNU Lesser General Public
|
|
17 * License along with this library; if not, write to the Free Software
|
|
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
19 * MA 02110-1301 USA
|
|
20 */
|
|
21
|
|
22 /**
|
|
23 * @class PieChart
|
|
24 * Implementation for a PieChart
|
|
25 * @author Sebastian Kruse (skruse@mpiwg-berlin.mpg.de)
|
|
26 *
|
|
27 * @param {HTML object} parent div to append the PieChart
|
|
28 */
|
|
29 function PieChart(parent, watchedDataset, watchedColumn, selectionFunction) {
|
|
30
|
|
31 if ((typeof selectionFunction !== "undefined") &&
|
|
32 (typeof selectionFunction.type !== "undefined") &&
|
|
33 (typeof selectionFunction.categories !== "undefined")){
|
|
34 this.type = selectionFunction.type;
|
|
35 this.categories = selectionFunction.categories;
|
|
36 }
|
|
37 this.pieChart = this;
|
|
38 this.pieChartDiv;
|
|
39 this.preHighlightObjects;
|
|
40 this.highlightedLabel;
|
|
41
|
|
42 this.informationDIV;
|
|
43 this.pieChartLabel;
|
|
44
|
|
45 this.parent = parent;
|
|
46 this.options = parent.options;
|
|
47
|
|
48 this.watchedDatasetObject;
|
|
49 this.watchedDataset = parseInt(watchedDataset);
|
|
50 this.watchColumn = watchedColumn;
|
|
51 if (typeof selectionFunction !== "undefined")
|
|
52 this.selectionFunction = selectionFunction;
|
|
53 else
|
|
54 //default selectionFunction returns value (creates "distinct" piechart)
|
|
55 this.selectionFunction = function(columnData){return columnData;};
|
|
56 }
|
|
57
|
|
58 PieChart.prototype = {
|
|
59
|
|
60 remove : function() {
|
|
61 for (var i = 0; i < this.parent.pieCharts.length; i++){
|
|
62 if (this.parent.pieCharts[i] === this)
|
|
63 this.parent.pieCharts[i] = null;
|
|
64 }
|
|
65 $(this.pieChartDiv).remove();
|
|
66 $(this.informationDIV).remove();
|
|
67 this.parent.redrawPieCharts();
|
|
68 },
|
|
69
|
|
70 refreshLabel : function(){
|
|
71 $(this.pieChartLabel).empty();
|
|
72 $(this.pieChartLabel).append(this.watchedDatasetObject.label + " - " + this.watchColumn);
|
|
73
|
|
74 var c = GeoTemConfig.getColor(this.watchedDataset);
|
|
75 $(this.pieChartLabel).css("color","rgb("+c.r1+","+c.g1+","+c.b1+")");
|
|
76 },
|
|
77
|
|
78 initialize : function() {
|
|
79 var pieChart = this;
|
|
80
|
|
81 if (typeof this.pieChartDiv === "undefined"){
|
|
82 this.informationDIV = document.createElement("div");
|
|
83 this.pieChartLabel = $("<span></span>");
|
|
84 $(this.informationDIV).append(this.pieChartLabel);
|
|
85 this.refreshLabel();
|
|
86
|
|
87 var removeButton = document.createElement("button");
|
|
88 $(this.informationDIV).append(removeButton);
|
|
89 $(removeButton).text("remove");
|
|
90 $(removeButton).click(function(){
|
|
91 pieChart.remove();
|
|
92 });
|
|
93
|
|
94 //only allow editing if it is a "manually" created piechart
|
|
95 //automatic (with a selection function) ones, can lead to numerous problems,
|
|
96 //e.g. too many categories or numeral categories threated as text ones
|
|
97 if ((typeof pieChart.type !== "undefined")&&
|
|
98 (typeof pieChart.categories !== "undefined")){
|
|
99 var editButton = document.createElement("button");
|
|
100 $(this.informationDIV).append(editButton);
|
|
101 $(editButton).text("edit");
|
|
102 $(editButton).click(function(){
|
|
103 var chooser = new PieChartCategoryChooser(
|
|
104 pieChart.parent,
|
|
105 pieChart.parent.options,
|
|
106 pieChart.watchedDataset,
|
|
107 pieChart.watchColumn,
|
|
108 pieChart.type,
|
|
109 pieChart.categories);
|
|
110 });
|
|
111
|
|
112 //add save button
|
|
113 if (pieChart.options.allowLocalStorage){
|
|
114 var saveButton = document.createElement("button");
|
|
115 $(this.informationDIV).append(saveButton);
|
|
116 $(saveButton).text("save");
|
|
117 $(saveButton).click(function(){
|
|
118 $( "<div>" +
|
|
119 "pie chart name : " +
|
|
120 "<input type='text' size=30 id='saveName' class='ui-widget-content ui-corner-all'></input>" +
|
|
121 "</div>").dialog({
|
|
122 width:'auto',
|
|
123 buttons: [
|
|
124 {
|
|
125 text: "save",
|
|
126 click: function(){
|
|
127 var saveName = $("#saveName").val();
|
|
128 var saveObject = new Object();
|
|
129 saveObject.type = pieChart.type;
|
|
130 saveObject.categories = pieChart.categories;
|
|
131 saveObject.columnName = pieChart.watchColumn;
|
|
132 //save to LocalStorage
|
|
133 $.remember({
|
|
134 name:pieChart.options.localStoragePrefix+saveName,
|
|
135 value:saveObject,
|
|
136 json:true
|
|
137 });
|
|
138 $(this).dialog( "close" );
|
|
139 }
|
|
140 }
|
|
141 ]
|
|
142 });
|
|
143
|
|
144 //set value to default (column name)
|
|
145 $("#saveName").val(pieChart.watchColumn);
|
|
146 //TODO: z-index has to be set, as the "tool-bars" of map (.ddbToolbar in style.css)
|
|
147 //also have a z-index of 10000. z-index should be removed from all elements.
|
|
148 $(".ui-dialog").css("z-index",10005);
|
|
149 });
|
|
150 }
|
|
151 }
|
|
152
|
|
153 $(this.parent.gui.pieChartsDiv).append(this.informationDIV);
|
|
154 this.pieChartDiv = document.createElement("div");
|
|
155 $(this.parent.gui.pieChartsDiv).append(this.pieChartDiv);
|
|
156
|
|
157 $(this.pieChartDiv).unbind();
|
|
158 $(this.pieChartDiv).bind("plothover", function (event, pos, item) {
|
|
159 var highlightedLabel;
|
|
160
|
|
161 if (item) {
|
|
162 highlightedLabel = item.series.label;
|
|
163 }
|
|
164 if (highlightedLabel !== pieChart.highlightedLabel){
|
|
165 pieChart.highlightedLabel = highlightedLabel;
|
|
166 pieChart.triggerHighlight(highlightedLabel);
|
|
167 }
|
|
168 });
|
|
169
|
|
170 $(this.pieChartDiv).bind("plotclick", function (event, pos, item) {
|
|
171 if (item) {
|
|
172 //item.series.label contains the column element
|
|
173 pieChart.triggerSelection(item.series.label);
|
|
174 } else {
|
|
175 pieChart.triggerSelection();
|
|
176 }
|
|
177 });
|
|
178 }
|
|
179 },
|
|
180
|
|
181 //check if dataset is still there
|
|
182 checkForDataSet : function() {
|
|
183 var datasets = this.parent.datasets;
|
|
184 if ((typeof datasets !== "undefined") && (typeof this.watchedDatasetObject !== "undefined")){
|
|
185 //check if our data went missing
|
|
186 for (var i = 0; i < datasets.length; i++){
|
|
187 if (datasets[i] === this.watchedDatasetObject){
|
|
188 //if dataset "before" this one was removed, the index changes
|
|
189 if (this.watchedDataset !== i){
|
|
190 //change color to the new one (changes with index!)
|
|
191 this.watchedDataset = i;
|
|
192 this.refreshLabel();
|
|
193 }
|
|
194 return true;
|
|
195 }
|
|
196 }
|
|
197 }
|
|
198 return false;
|
|
199 },
|
|
200
|
|
201 initPieChart : function(dataSets) {
|
|
202 // get dataset object (could not be there on startup, e.g. piechart defined before load completes)
|
|
203 if (typeof this.watchedDatasetObject === "undefined")
|
|
204 this.watchedDatasetObject = this.parent.datasets[this.watchedDataset];
|
|
205
|
|
206 this.initialize();
|
|
207
|
|
208 // if our dataset went missing, remove this piechart
|
|
209 if (!this.checkForDataSet()){
|
|
210 this.remove();
|
|
211 return;
|
|
212 }
|
|
213
|
|
214 var objects = [];
|
|
215 for (var i = 0; i < dataSets.length; i++)
|
|
216 objects.push([]);
|
|
217 objects[this.watchedDataset] = dataSets[this.watchedDataset].objects;
|
|
218
|
|
219 this.preHighlightObjects = objects;
|
|
220 this.redrawPieChart(objects);
|
|
221 },
|
|
222
|
|
223 redrawPieChart : function(objects) {
|
|
224
|
|
225 if (typeof objects === "undefined")
|
|
226 objects = this.preHighlightObjects;
|
|
227
|
|
228 if (this.checkForDataSet(objects)){
|
|
229 var pieChart = this;
|
|
230 if (objects[this.watchedDataset].length === 0)
|
|
231 objects = this.preHighlightObjects;
|
|
232
|
|
233 var calculateSlices = function(dataObjects){
|
|
234 var chartDataCounter = new Object;
|
|
235
|
|
236 $(dataObjects).each(function(){
|
|
237 var columnData = pieChart.parent.getElementData(this, pieChart.watchColumn, pieChart.selectionFunction);
|
|
238
|
|
239 //disregard empty cells
|
|
240 if ( (typeof columnData === "undefined") || (columnData == "") )
|
|
241 return;
|
|
242
|
|
243 var weight = this.weight;
|
|
244
|
|
245 if (typeof chartDataCounter[columnData] === "undefined")
|
|
246 chartDataCounter[columnData] = weight;
|
|
247 else
|
|
248 chartDataCounter[columnData] += weight;
|
|
249 });
|
|
250
|
|
251 var chartData = [];
|
|
252 $.each(chartDataCounter, function(name,val){
|
|
253 //get rgb-color (24bit = 6 hex digits) from hash
|
|
254 var color = '#'+hex_md5(name).substr(0,6);
|
|
255 chartData.push({label:name,data:val,color:color});
|
|
256 });
|
|
257
|
|
258 //sort by count (occurances of category)
|
|
259 var sortByVal = function(a,b){
|
|
260 return (b.data-a.data);
|
|
261 };
|
|
262 chartData.sort(sortByVal);
|
|
263
|
|
264 return chartData;
|
|
265 };
|
|
266
|
|
267 var chartData = calculateSlices(objects[this.watchedDataset]);
|
|
268
|
|
269 if (chartData.length>0){
|
|
270 $(this.pieChartDiv).empty();
|
|
271
|
|
272 //calculate height (flot NEEDS a height)
|
|
273 var parentHeight = $(this.parent.gui.pieChartsDiv).outerHeight(true) - $(this.parent.gui.columnSelectorDiv).outerHeight(true);
|
|
274 var pieChartCount = 0;
|
|
275 $(this.parent.pieCharts).each(function(){
|
|
276 if (this instanceof PieChart)
|
|
277 pieChartCount++;
|
|
278 });
|
|
279 var height = (parentHeight/pieChartCount) - $(this.informationDIV).outerHeight(true);
|
|
280 if (pieChart.options.restrictPieChartSize !== false)
|
|
281 height = Math.min(height, $(window).height() * pieChart.options.restrictPieChartSize);
|
|
282 $(this.pieChartDiv).height(height);
|
|
283
|
|
284 $.plot($(this.pieChartDiv), chartData,
|
|
285 {
|
|
286 series: {
|
|
287 // Make this a pie chart.
|
|
288 pie: {
|
|
289 show:true
|
|
290 }
|
|
291 },
|
|
292 legend: { show:true, position: 'se' },
|
|
293 grid: {
|
|
294 hoverable: true,
|
|
295 clickable: true
|
|
296 },
|
|
297 tooltip: true,
|
|
298 tooltipOpts: {
|
|
299 content: "%s %p.1%"
|
|
300 }
|
|
301 }
|
|
302 );
|
|
303 }
|
|
304 }
|
|
305 },
|
|
306
|
|
307 triggerHighlight : function(columnElement) {
|
|
308 var highlightedObjects = [];
|
|
309 for (var i = 0; i < GeoTemConfig.datasets.length; i++)
|
|
310 highlightedObjects.push([]);
|
|
311
|
|
312 if (this.watchedDataset >= 0)
|
|
313 highlightedObjects[this.watchedDataset] =
|
|
314 this.parent.getElementsByValue(columnElement, this.watchedDataset, this.watchColumn, this.selectionFunction);
|
|
315 else
|
|
316 highlightedObjects[this.watchedDataset] = [];
|
|
317
|
|
318 this.parent.core.triggerHighlight(highlightedObjects);
|
|
319
|
|
320 var pieChart = this;
|
|
321 $(this.parent.pieCharts).each(function(){
|
|
322 if (this instanceof PieChart && (this !== pieChart)){
|
|
323 if (this.watchedDataset === pieChart.watchedDataset)
|
|
324 this.redrawPieChart(highlightedObjects);
|
|
325 }
|
|
326 });
|
|
327 },
|
|
328
|
|
329 triggerSelection : function(columnElement) {
|
|
330 var selectedObjects = [];
|
|
331 for (var i = 0; i < GeoTemConfig.datasets.length; i++)
|
|
332 selectedObjects.push([]);
|
|
333
|
|
334 var selection;
|
|
335 if (typeof columnElement !== "undefined"){
|
|
336 selectedObjects[this.watchedDataset] =
|
|
337 this.parent.getElementsByValue(columnElement, this.watchedDataset, this.watchColumn, this.selectionFunction);
|
|
338 selection = new Selection(selectedObjects, this);
|
|
339 } else {
|
|
340 selection = new Selection(selectedObjects);
|
|
341 }
|
|
342
|
|
343 this.parent.core.triggerSelection(selection);
|
|
344
|
|
345 if (!selection.valid()){
|
|
346 selection.loadAllObjects();
|
|
347 //"undo" selection (click next to piechart)
|
|
348 //so also redraw this dataset
|
|
349 this.preHighlightObjects = selection.objects;
|
|
350 this.redrawPieChart(selection.objects);
|
|
351 }
|
|
352
|
|
353 var pieChart = this;
|
|
354 $(this.parent.pieCharts).each(function(){
|
|
355 if (this instanceof PieChart && (this !== pieChart)){
|
|
356 if (this.watchedDataset === pieChart.watchedDataset){
|
|
357 this.preHighlightObjects = selection.objects;
|
|
358 this.redrawPieChart(selection.objects);
|
|
359 }
|
|
360 }
|
|
361 });
|
|
362 },
|
|
363
|
|
364 deselection : function() {
|
|
365 },
|
|
366
|
|
367 filtering : function() {
|
|
368 },
|
|
369
|
|
370 inverseFiltering : function() {
|
|
371 },
|
|
372
|
|
373 triggerRefining : function() {
|
|
374 },
|
|
375
|
|
376 reset : function() {
|
|
377 },
|
|
378
|
|
379 show : function() {
|
|
380 },
|
|
381
|
|
382 hide : function() {
|
|
383 }
|
|
384 };
|