diff geotemco/js/FuzzyTimeline/FuzzyTimelineRangeBars.js @ 0:b12c99b7c3f0

commit for previous development
author Zoe Hong <zhong@mpiwg-berlin.mpg.de>
date Mon, 19 Jan 2015 17:13:49 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/geotemco/js/FuzzyTimeline/FuzzyTimelineRangeBars.js	Mon Jan 19 17:13:49 2015 +0100
@@ -0,0 +1,472 @@
+/*
+* FuzzyTimelineRangeBars.js
+*
+* Copyright (c) 2013, Sebastian Kruse. All rights reserved.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or (at your option) any later version.
+*
+* This library 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
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+* MA 02110-1301  USA
+*/
+
+/**
+ * @class FuzzyTimelineRangeBars
+ * Implementation for a fuzzy time-ranges barchart
+ * @author Sebastian Kruse (skruse@mpiwg-berlin.mpg.de)
+ *
+ * @param {HTML object} parent div to append the FuzzyTimeline
+ */
+function FuzzyTimelineRangeBars(parent) {
+
+	this.rangeBars = this;
+	
+	this.parent = parent;
+	this.options = parent.options;
+	
+	this.datasets;
+	//contains selected data
+	this.selected = undefined;
+	
+	this.datasetsPlot;
+	this.highlightedDatasetsPlot;
+	this.yValMin;
+	this.yValMax;
+	this.displayType;
+	
+	this.plotDiv = this.parent.gui.plotDiv;
+
+	this.spanWidth;
+	this.tickSpans;
+	this.plot;
+}
+
+FuzzyTimelineRangeBars.prototype = {
+
+	initialize : function(datasets) {
+		var rangeBar = this;
+		
+		rangeBar.datasets = datasets;
+		rangeBar.selected = [];
+	},
+	
+	createPlot : function(datasets) {
+		var rangeBar = this;
+		var plots = [];
+		var objectHashes = [];
+		
+		//-1 because last span is always empty (only there to have the ending date)
+		var tickCount = rangeBar.tickSpans.length-1;
+		
+		$(datasets).each(function(){
+			var chartDataCounter = [];
+			var objectHash = new Object();
+			
+			for (var i = 0; i < tickCount; i++){
+				chartDataCounter[i]=0;
+			}
+			//check if we got "real" datasets, or just array of objects
+			var datasetObjects = this;
+			if (typeof this.objects !== "undefined")
+				datasetObjects = this.objects;
+			$(datasetObjects).each(function(){
+				var ticks = rangeBar.parent.getTicks(this, rangeBar.spanWidth);
+				if (typeof ticks !== "undefined"){
+					var exactTickCount = 
+						ticks.firstTickPercentage+
+						ticks.lastTickPercentage+
+						(ticks.lastTick-ticks.firstTick-1);
+					for (var i = ticks.firstTick; i <= ticks.lastTick; i++){
+						var weight = 0;
+						//calculate the weight for each span, that the object overlaps
+						if (rangeBar.parent.options.timelineMode == 'fuzzy'){
+							//in fuzzy mode, each span gets just a fraction of the complete weight
+							if (i == ticks.firstTick)
+								weight = this.weight * ticks.firstTickPercentage/exactTickCount;
+							else if (i == ticks.lastTick)
+								weight = this.weight * ticks.lastTickPercentage/exactTickCount;
+							else
+								weight = this.weight * 1/exactTickCount;
+						} else if (rangeBar.parent.options.timelineMode == 'stacking'){
+							//in stacking mode each span gets the same amount.
+							//(besides first and last..)
+							if (i == ticks.firstTick)
+								weight = this.weight * ticks.firstTickPercentage;
+							else if (i == ticks.lastTick)
+								weight = this.weight * ticks.lastTickPercentage;
+							else
+								weight = this.weight;
+							
+							weight = this.weight;
+						}
+
+						chartDataCounter[i] += weight;
+						//add this object to the hash
+						if (typeof objectHash[i] === "undefined")
+							objectHash[i] = [];
+						objectHash[i].push(this);
+					}
+				}
+			});
+			
+			//scale according to selected type
+			chartDataCounter = rangeBar.parent.scaleData(chartDataCounter);
+			
+			//transform data so it can be passed to the flot barchart
+			var plotData = [];
+			for (var i = 0; i < tickCount; i++){
+				plotData[i] = [];
+				plotData[i][0] = i;
+				plotData[i][1] = chartDataCounter[i];
+			}
+			
+			//delete bars with 0 values
+			for (var i = 0; i < tickCount; i++){
+				if (plotData[i][1]==0)
+					delete plotData[i];
+			}
+			
+			plots.push(plotData);
+			objectHashes.push(objectHash);
+		});
+		
+		return {plots:plots, hashs:objectHashes};
+	},
+	
+	showPlot : function(){
+		var rangeBar = this;
+		var plot = rangeBar.datasetsPlot;
+		var highlight_select_plot = $.merge([],plot);
+		
+		//see if there are selected/highlighted values
+		if (rangeBar.highlightedDatasetsPlot instanceof Array){
+			//check if plot is some other - external - graph
+			if (plot === rangeBar.datasetsPlot)
+				highlight_select_plot = $.merge(highlight_select_plot,rangeBar.highlightedDatasetsPlot);
+		}
+		
+		var tickCount = rangeBar.tickSpans.length-1;
+		var ticks = [];
+		
+		var axisFormatString = "YYYY";
+		if (rangeBar.spanWidth<60*1000){
+			axisFormatString = "YYYY/MM/DD HH:mm:ss";
+		} else if (rangeBar.spanWidth<60*60*1000) {
+			axisFormatString = "YYYY/MM/DD HH:mm";
+		} else if (rangeBar.spanWidth<24*60*60*1000){
+			axisFormatString = "YYYY/MM/DD HH";
+		} else if (rangeBar.spanWidth<31*24*60*60*1000){
+			axisFormatString = "YYYY/MM/DD";
+		} else if (rangeBar.spanWidth<12*31*24*60*60*1000){
+			axisFormatString = "YYYY/MM";
+		}
+		//only show ~10 labels on the x-Axis (increase if zoomed)
+		var labelModulo = Math.ceil(tickCount/(10*rangeBar.parent.zoomFactor));
+		for (var i = 0; i < tickCount; i++){
+			var tickLabel = "";
+			if (i%labelModulo==0){
+				tickLabel = rangeBar.tickSpans[i].format(axisFormatString);
+			}
+			while ((tickLabel.length > 1) && (tickLabel.indexOf("0")==0))
+				tickLabel = tickLabel.substring(1);
+			ticks[i] = [i,tickLabel];
+		}
+		
+		var options = {
+				series:{
+	                bars:{show: true}
+	            },
+				grid: {
+		            hoverable: true,
+		            clickable: true,
+		            backgroundColor: rangeBar.parent.options.backgroundColor,
+		            borderWidth: 0,
+		            minBorderMargin: 0,
+		        },
+		        xaxis: {          
+		        	ticks: ticks,
+		        	min : 0, 
+					max : tickCount,
+		        },
+		        yaxis: {
+		        	min : rangeBar.yValMin,
+		        	max : rangeBar.yValMax*1.05
+		        },
+		        tooltip: true,
+		        tooltipOpts: {
+		            content: function(label, xval, yval, flotItem){
+		    			var fromLabel = rangeBar.tickSpans[xval].format(axisFormatString);
+		    			while ((fromLabel.length > 1) && (fromLabel.indexOf("0")==0))
+		    				fromLabel = fromLabel.substring(1);
+		    			var toLabel = rangeBar.tickSpans[xval+1].clone().subtract("ms",1).format(axisFormatString);
+		    			while ((toLabel.length > 1) && (toLabel.indexOf("0")==0))
+		    				toLabel = toLabel.substring(1);
+		            	highlightString =	fromLabel + " - " + toLabel + " : ";
+		            	//(max.)2 Nachkomma-Stellen von y-Wert anzeigen
+		            	highlightString +=	Math.round(yval*100)/100; 
+
+		        		return highlightString;
+		            }
+		        },
+		        selection: { 
+		        	mode: "x"
+		        }
+			};
+		if (!rangeBar.parent.options.showYAxis)
+			options.yaxis.show=false;
+		
+		var highlight_select_plot_colors = [];		
+		var i = 0;
+		$(highlight_select_plot).each(function(){
+			var color;
+			if (i < GeoTemConfig.datasets.length){
+				var datasetColors = GeoTemConfig.getColor(i);
+				if (highlight_select_plot.length>GeoTemConfig.datasets.length)
+					color = "rgb("+datasetColors.r0+","+datasetColors.g0+","+datasetColors.b0+")";
+				else 
+					color = "rgb("+datasetColors.r1+","+datasetColors.g1+","+datasetColors.b1+")";
+			} else {
+				var datasetColors = GeoTemConfig.getColor(i-GeoTemConfig.datasets.length);
+				color = "rgb("+datasetColors.r1+","+datasetColors.g1+","+datasetColors.b1+")";
+			}			
+			
+			highlight_select_plot_colors.push({
+				color : color,
+				data : this
+			});
+			i++;
+		});		
+		
+		$(rangeBar.plotDiv).unbind();		
+		rangeBar.plot = $.plot($(rangeBar.plotDiv), highlight_select_plot_colors, options);
+		rangeBar.parent.drawHandles();
+		
+		var density = rangeBar.parent.density;
+		if (typeof density !== "undefined")
+			$(rangeBar.plotDiv).unbind("plothover", density.hoverFunction);
+		$(rangeBar.plotDiv).unbind("plothover", rangeBar.hoverFunction);
+	    $(rangeBar.plotDiv).bind("plothover", $.proxy(rangeBar.hoverFunction,rangeBar));
+
+	    //this var prevents the execution of the plotclick event after a select event 
+	    rangeBar.wasSelection = false;
+		$(rangeBar.plotDiv).unbind("plotclick");
+	    $(rangeBar.plotDiv).bind("plotclick", $.proxy(rangeBar.clickFunction,rangeBar));
+	    
+	    $(rangeBar.plotDiv).unbind("plotselected");
+	    $(rangeBar.plotDiv).bind("plotselected", $.proxy(rangeBar.selectFunction,rangeBar));	
+	},
+	
+	hoverFunction : function (event, pos, item) {
+		var rangeBar = this;
+    	var hoverBar;
+    	var spans;
+        if (item) {
+        	hoverBar = item.datapoint[0];
+        }
+        //remember last date, so that we don't redraw the current state
+        //that date may be undefined is on purpose
+    	if (rangeBar.highlighted !== hoverBar){
+    		rangeBar.highlighted = hoverBar;
+    		if (typeof hoverBar === "undefined")
+    			rangeBar.triggerHighlight();
+    		else
+    			rangeBar.triggerHighlight(hoverBar);
+        }
+    },
+	
+    clickFunction : function (event, pos, item) {
+    	var rangeBar = this;
+    	if (rangeBar.wasSelection)
+    		rangeBar.wasSelection = false;
+    	else {
+        	//remove selection handles (if there were any)
+        	rangeBar.parent.clearHandles();
+        	
+	    	var clickBar;
+	        if (item) {
+				//contains the x-value (date)
+	        	clickBar = item.datapoint[0];
+	        }  	
+    		if (typeof clickBar === "undefined")
+    			rangeBar.triggerSelection();
+    		else
+    			rangeBar.triggerSelection(clickBar);
+        	wasDataClick = true;
+        }
+    },
+    
+    selectFunction : function(event, ranges) {
+    	var rangeBar = this;
+    	startBar = Math.floor(ranges.xaxis.from);
+    	endBar = Math.floor(ranges.xaxis.to);
+    	rangeBar.triggerSelection(startBar, endBar);
+    	rangeBar.wasSelection = true;
+    	
+    	rangeBar.parent.clearHandles();
+    	var xaxis = rangeBar.plot.getAxes().xaxis;
+    	var x1 = rangeBar.plot.pointOffset({x:ranges.xaxis.from,y:0}).left;
+    	var x2 = rangeBar.plot.pointOffset({x:ranges.xaxis.to,y:0}).left;
+    	rangeBar.parent.addHandle(x1,x2);
+    },
+    
+	selectByX : function(x1, x2){
+		rangeBar = this;
+		var xaxis = rangeBar.plot.getAxes().xaxis;
+		var offset = rangeBar.plot.getPlotOffset().left;
+    	var from = Math.floor(xaxis.c2p(x1-offset));
+    	var to = Math.floor(xaxis.c2p(x2-offset));
+    	
+		rangeBar.triggerSelection(from, to);
+	},	
+	
+	drawRangeBarChart : function(datasets, spanWidth){
+		var rangeBar = this;
+		rangeBar.spanWidth = spanWidth; 
+		rangeBar.tickSpans = rangeBar.parent.getSpanArray(rangeBar.spanWidth);
+		//-1 because last span is always empty (only there to have the ending date)
+		var tickCount = rangeBar.tickSpans.length-1;
+		
+		if (tickCount > rangeBar.options.maxBars){
+			var zoomFactor = tickCount / rangeBar.options.maxBars;
+			rangeBar.parent.zoomPlot(zoomFactor);
+		} else
+			rangeBar.parent.zoomPlot(1);
+		
+		rangeBar.yValMin = 0;
+		rangeBar.yValMax = 0;
+		
+		var plotAndHash = rangeBar.createPlot(datasets);
+		rangeBar.datasetsPlot = plotAndHash.plots;
+		rangeBar.datasetsHash = plotAndHash.hashs;
+		delete rangeBar.highlightedDatasetsPlot;
+		//redraw selected plot to fit (possible) new scale
+		rangeBar.selectionChanged(rangeBar.selected);
+		
+		//get min and max values
+		for (var i = 0; i < rangeBar.datasetsPlot.length; i++){
+			for (var j = 0; j < rangeBar.datasetsPlot[i].length; j++){
+				if (typeof rangeBar.datasetsPlot[i][j] !== "undefined"){
+					var val = rangeBar.datasetsPlot[i][j][1];
+					
+					if (val < rangeBar.yValMin)
+						rangeBar.yValMin = val;
+					if (val > rangeBar.yValMax)
+						rangeBar.yValMax = val;
+				}
+			}
+		}
+		
+		rangeBar.showPlot();
+	},
+	
+	highlightChanged : function(objects) {
+		if( !GeoTemConfig.highlightEvents ){
+			return;
+		}
+		var rangeBar = this;
+		var emptyHighlight = true;
+		var selected_highlighted = objects;
+		if (typeof rangeBar.selected !== "undefined")
+			var selected_highlighted = GeoTemConfig.mergeObjects(objects,rangeBar.selected);
+		$(selected_highlighted).each(function(){
+			if ((this instanceof Array) && (this.length > 0)){
+				emptyHighlight = false;
+				return false;
+			}
+		});
+		if (emptyHighlight && (typeof rangeBar.selected === "undefined")){
+			rangeBar.highlightedDatasetsPlot = [];
+		} else {
+			rangeBar.highlightedDatasetsPlot = rangeBar.createPlot(selected_highlighted).plots;
+		}			
+		rangeBar.showPlot();
+	},
+	
+	selectionChanged : function(objects) {
+		if( !GeoTemConfig.selectionEvents ){
+			return;
+		}
+		var rangeBar = this;
+		rangeBar.selected = objects;
+		rangeBar.highlightChanged([]);
+	},
+	
+	triggerHighlight : function(hoverPoint) {
+		var rangeBar = this;
+		var highlightedObjects = [];
+		
+		if (typeof hoverPoint !== "undefined"){
+			$(rangeBar.datasetsHash).each(function(){
+				if (typeof this[hoverPoint] !== "undefined")
+					highlightedObjects.push(this[hoverPoint]);
+				else
+					highlightedObjects.push([]);
+			});
+		} else {
+			for (var i = 0; i < GeoTemConfig.datasets.length; i++)
+				highlightedObjects.push([]);
+		}
+		
+		this.parent.core.triggerHighlight(highlightedObjects);
+	},
+
+	triggerSelection : function(startBar, endBar) {
+		var rangeBar = this;
+		var selection;
+		if (typeof startBar !== "undefined") {
+			if (typeof endBar === "undefined")
+				endBar = startBar;
+			rangeBar.selected = [];
+			$(rangeBar.datasetsHash).each(function(){
+				var objects = [];
+				for (var i = startBar; i <= endBar; i++){
+					$(this[i]).each(function(){
+						if ($.inArray(this, objects) == -1){
+							objects.push(this);
+						}
+					});
+				}				
+				rangeBar.selected.push(objects);
+			});
+			selection = new Selection(rangeBar.selected, rangeBar.parent);
+		} else {
+			rangeBar.selected = [];
+			for (var i = 0; i < GeoTemConfig.datasets.length; i++)
+				rangeBar.selected.push([]);
+			selection = new Selection(rangeBar.selected);
+		}
+		
+		rangeBar.parent.selectionChanged(selection);
+		rangeBar.parent.core.triggerSelection(selection);
+	},
+
+	deselection : function() {
+	},
+
+	filtering : function() {
+	},
+
+	inverseFiltering : function() {
+	},
+
+	triggerRefining : function() {
+	},
+
+	reset : function() {
+	},
+	
+	show : function() {		
+	},
+
+	hide : function() {
+	}
+};