This page allows to visualize and to interactively explore the interval approach with time windows. At the bottom of this page there is a text box with data stream elements (feel free to insert your own elements). Each line is one element. Fields are separated by commas. The first field is the timestamp, the second is the value that will be displayed in the visualization. All other values will be ignored. Bush the button "Add Next Element" to add the first (next) element of the text field to the visualization area (next to this paragraph that is still blank yet). The validity interval of the elements will be depicted as lines with respect to the width and advance specified in the text boxes below. After adding a bunch of elements the visualization highlights the range of a window and all elements that are part of the highlighted window. Move the mouse pointer to a point in time to change the observation point.

<div id="scrollcontainer" style="overflow:scroll; height:500px">

<canvas id="canvas">

Your browser does not support the HTML5 canvas tag.

</canvas>

</div>


<input type="button" value="Add Next Element" onclick="addNextElement();">

<input type="button" value="Add All Elements" onclick="addAllElements();">

<input type="button" value="Reset Drawing" onclick="reset();">


<table>

<tr><td>Observation Point in Time:</td><td id="observationPoint"><td></tr>

<tr><td>Elements That Start at the Observation Point:</td><td id="elementsStart"><td></tr>

<tr><td>Passed Time Units in Window:</td><td id="actualWindowWidth"><td></tr>

<tr><td>Elements in Window:</td><td id="elementsInWindow"><td></tr>

<tr><td>Count:</td><td id="count"><td></tr>

<tr><td>Sum:</td><td id="sum"><td></tr>

<tr><td>Avg:</td><td id="avg"><td></tr>

</table>




<table>

<tr>

<td>Window Width:</td><td><input id="window_width" value="10" /></td>

</tr>

<tr>

<td>Window Advance:</td><td><input id="window_advance" value="1" /></td>

</tr>

</table>


















<h2>Elements queue:</h2> 

<p>Samples: <select id="samples" size="1">

    <option value="1" selected="selected">Sample 1</option>

    <option value="2">Sample 2</option>

    <option value="3">Random</option>

</select> <input type="button" value="Set Sample Elements" onclick="setSample();"></p>

<pre>timestamp,display value[, other values]</pre>

<textarea id="elements" style="width: 300px; height: 150px;"></textarea>

<div id="infobox"></div>




<script>




var elements = [];



setInfoText = function(infotext) {

	var infobox = document.getElementById("infobox");

	infobox.innerHTML = "<p>" + infotext + "</p>" + infobox.innerHTML;

}




firstElement = function() {

	return elements[0];

}




lastElement = function() {

	return elements[elements.length-1];

}




startTsOfElement = function(element) {

	return parseInt(element.split(",")[0]);

}




endTsOfElement = function(element) {

	var window_width = parseInt(document.getElementById("window_width").value);

	var window_advance = parseInt(document.getElementById("window_advance").value);

	return Math.floor(startTsOfElement(element)/window_advance) * window_advance + window_width;

}




valueOfElement = function(element) {

	return element.split(",")[1];

}




posXOfElement = function(element) {

	return (startTsOfElement(element) - startTsOfElement(firstElement()))*50+25;

}




posYOfElement = function(element) {

	return 10;

}




posXStartOfValidity = function(element) {

	return posXOfElement(element);

}




posYStartOfValidity = function(element) {

	var idx = elements.indexOf(element);

	return 50 + (idx*20);

}







posXEndOfValidity = function(element) {

	return (endTsOfElement(element) - startTsOfElement(firstElement()))*50+25;

}




posYEndOfValidity = function(element) {

	return posYStartOfValidity(element);

}




setCanvasSize = function() {

	var canvas = document.getElementById('canvas');

	canvas.width = posXEndOfValidity(lastElement()) + 100;

	canvas.height = posYEndOfValidity(lastElement()) + 50;

}




drawGrid = function(ctx, observationPoint) {

	var timevalue = parseInt(startTsOfElement(firstElement()));

	ctx.font = "15px Arial";

	ctx.textAlign = 'left';

  	ctx.textBaseline = 'middle';

	ctx.fillText("Application Time t", 3, posYEndOfValidity(lastElement())+10);




	for(i = 0; i <= posXEndOfValidity(lastElement()) + 50; i = i + 50) {

		if(timevalue == observationPoint) {

			ctx.fillStyle = "rgba(0, 0, 255, 0.5)";

			

			ctx.rect(i, 0, 50, posYEndOfValidity(lastElement()) + 50);

			ctx.fill();

			ctx.fillStyle = 'blue';

		} else {

			ctx.fillStyle = 'black';

		}

		

		

		

		ctx.lineWidth=0.5;

		

		ctx.beginPath();

		ctx.moveTo(i,0);

		ctx.lineTo(i,posYEndOfValidity(lastElement()) + 50);

		ctx.stroke();

		

		ctx.font = "20px Arial";

		ctx.textAlign = 'center';

	  	ctx.textBaseline = 'middle';

		ctx.fillText(timevalue, i+25, posYEndOfValidity(lastElement()) + 40);

		timevalue = timevalue + 1;

	}

	

	ctx.beginPath();

	ctx.moveTo(0,posYEndOfValidity(lastElement()) + 20);

	ctx.lineTo(i,posYEndOfValidity(lastElement()) + 20);

	ctx.stroke();

}




isInWindow = function(element, observationPoint) {

	return parseInt(startTsOfElement(element)) <= observationPoint && parseInt(endTsOfElement(element)) > observationPoint;

}




elementsInWindow = function(observationPoint) {

	var elementsInWindow = [];

	for(i = 0; i < elements.length; ++i) {

		if(isInWindow(elements[i], observationPoint)) {

			elementsInWindow.push(elements[i]);

		}

	}

	return elementsInWindow;

}




drawElements = function(ctx, observationPoint) {

	for(i = 0; i < elements.length; ++i) {

		if(isInWindow(elements[i], observationPoint)) {

			ctx.fillStyle = 'blue';

			ctx.strokeStyle = '#0000ff';

			ctx.lineWidth=3;

			ctx.font = "bold 20px Arial";

		} else {

			ctx.fillStyle = 'black';

			ctx.strokeStyle = '#000000';

			ctx.lineWidth=1;

			ctx.font = "20px Arial";

		}

		

		

		

		if(i > 0 && startTsOfElement(elements[i]) == startTsOfElement(elements[i-1])) {

			if(i > 1 && startTsOfElement(elements[i]) == startTsOfElement(elements[i-2])) {

				// "+" has been printed already

			} else {

				ctx.textAlign = 'right';

			  	ctx.textBaseline = 'middle';

				ctx.fillText("+", posXOfElement(elements[i])+25, posYOfElement(elements[i]));

			}

		} else {

			ctx.textAlign = 'center';

		  	ctx.textBaseline = 'middle';

			ctx.fillText(valueOfElement(elements[i]), posXOfElement(elements[i]), posYOfElement(elements[i]));

		}

		

		ctx.beginPath();

		ctx.moveTo(posXStartOfValidity(elements[i]), posYStartOfValidity(elements[i]));

		ctx.lineTo(posXEndOfValidity(elements[i]), posYEndOfValidity(elements[i]));

		ctx.stroke();

		

		ctx.beginPath(); 

		ctx.arc(posXStartOfValidity(elements[i]), posYStartOfValidity(elements[i]), 5, 0, Math.PI*2, true);  

		ctx.closePath();

		ctx.fill();

		

		ctx.beginPath(); 

		ctx.arc(posXEndOfValidity(elements[i]), posYEndOfValidity(elements[i]), 5, 0, Math.PI*2, true);  

		ctx.closePath();

		ctx.fillStyle = 'white';

		ctx.fill();

		ctx.stroke();

	}

	ctx.fillStyle = 'black';

	ctx.strokeStyle = '#000000';

	ctx.lineWidth=1;

}




elementsStartAt = function(observationPoint) {

	var e = [];

	for(i = 0; i < elements.length; ++i) {

		if(startTsOfElement(elements[i]) == observationPoint) {

			e.push(elements[i]);

		}

	}

	return e;

}




setInfoData = function(observationPoint) {

	document.getElementById("observationPoint").innerHTML = observationPoint;

	document.getElementById("elementsStart").innerHTML = "[" + elementsStartAt(observationPoint).join("]; [") + "]";

	var allElementsInWindow = elementsInWindow(observationPoint);

	document.getElementById("elementsInWindow").innerHTML = "[" + allElementsInWindow.join("]; [") + "]";

	document.getElementById("count").innerHTML = allElementsInWindow.length;

	var sum = 0;

	for(i = 0; i < allElementsInWindow.length; ++i) {

		sum = sum + parseInt(valueOfElement(allElementsInWindow[i]));

	}

	document.getElementById("avg").innerHTML = sum / allElementsInWindow.length;

	document.getElementById("sum").innerHTML = sum;

}




drawWindowBox = function(ctx, observationPoint) {

	var ts;

	var window_width = parseInt(document.getElementById("window_width").value);

	for(i = startTsOfElement(firstElement()) - window_width; i <= observationPoint; ++i) {

		if(isInWindow(i + ",0", observationPoint)) {

			ts = i;

			break;

		}

	}

	ctx.strokeStyle = '#0000ff';

	ctx.lineWidth=3;

	ctx.beginPath(); 

	ctx.rect(posXOfElement(ts + ",0")-25, 0, posXOfElement((ts + window_width ) + ",0")-posXOfElement(ts + ",0"), 20);

	ctx.stroke();

	

	document.getElementById("actualWindowWidth").innerHTML = observationPoint - ts + 1;

}




draw = function(observationPoint) {

	observationPoint = observationPoint || startTsOfElement(lastElement());

	setInfoData(observationPoint);

	var ctx = document.getElementById('canvas').getContext('2d');

	setCanvasSize();

	drawGrid(ctx, observationPoint);

	drawElements(ctx, observationPoint);

	drawWindowBox(ctx, observationPoint);

}




function scrollToLastElement() {

	var div = document.getElementById("scrollcontainer");

	var scrollWidth = Math.max(div.scrollWidth, div.clientWidth);

	div.scrollLeft = scrollWidth - div.clientWidth;

}




addNextElement = function() {

	var nextElements = document.getElementById("elements").value.trim().split("\n");

	if(nextElements.length > 0 && nextElements[nextElements.length-1].trim().length > 0) {

		if(elements.length > 0 && startTsOfElement(lastElement()) > startTsOfElement(nextElements[0])) {

			// reset if the new element is not temporal after the current drawn elements

			reset();

		}

		elements.push(nextElements.shift());

		setInfoText("Element <em>" + elements[elements.length-1] + "</em> added.");

		document.getElementById("elements").value = nextElements.join("\n");

		draw();

		scrollToLastElement();

	} else {

		setInfoText("No next element.");

	}

}




addAllElements = function() {

	var nextElements = document.getElementById("elements").value.trim().split("\n");

	if(nextElements.length > 0 && nextElements[nextElements.length-1].trim().length > 0) {

		if(elements.length > 0 && startTsOfElement(lastElement()) > startTsOfElement(nextElements[0])) {

			// reset if the new elements are not temporal after the current drawn elements

			reset();

		}

		elements = elements.concat(nextElements);

		document.getElementById("elements").value = "";

		setInfoText(nextElements.length + " elements added.");

		draw();

		scrollToLastElement();

	} else {

		setInfoText("No elements.");

	}

}




mousePosToObservationPoint = function(mouseX, mouseY) {

	if(elements.length > 0) {

		return Math.floor(mouseX/50) + parseInt(startTsOfElement(firstElement()));

	} else {

		return 0;

	}

}




canvas = document.getElementById('canvas');

canvas.onmousemove = mousePos;




function mousePos(e) {

    if (e.offsetX) {

        mouseX = e.offsetX;

        mouseY = e.offsetY;

    }

    else if (e.layerX) {

        mouseX = e.layerX;

        mouseY = e.layerY;

    }

    var x = mousePosToObservationPoint(mouseX, mouseY);

    if(x > 0) {

		draw(x);

	}

}




setSample();




var randomTs = 10;




function setSample() {

	var sampleId = document.getElementById("samples").value;

	switch(parseInt(sampleId)) {

		case 1:

			var sample = "59,15\n61,16\n62,17\n62,18\n65,19\n66,20\n68,21\n70,11\n71,56\n72,54\n74,23\n80,34\n81,73\n82,11\n87,24";

			break;

		case 2:

			var e = [];

			for(i = 10; i < 100; ++i) {

				e.push(i + "," + i);

			}

			var sample = e.join("\n");

			break;

		case 3:

			var e = [];

			for(i = 1; i < 20; ++i) {

				randomTs = randomTs + Math.floor((Math.random() * 5) + 1);

				e.push(randomTs + "," + Math.floor((Math.random() * 99) + 1));

			}

			

			var sample = e.join("\n");

			break;

	}

	document.getElementById("elements").value = sample;

}




function reset() {

	elements = [];

	var canvas = document.getElementById('canvas');

	var ctx = document.getElementById('canvas').getContext('2d');

	ctx.clearRect(0, 0, canvas.width, canvas.height);

}

var canvas = document.getElementById('canvas');
canvas.width = 500;
var ctx = document.getElementById('canvas').getContext('2d');
ctx.font = "20px Arial";
ctx.textAlign = 'left';
ctx.fillText("To start the visualization:", 20, 100);
ctx.fillText("Push button below to add elements.", 20, 150);
</script>