Flot Selection to Control multiple charts - flot

I've been trying to modify the code from the flot examples (http://www.flotcharts.org/flot/examples/zooming/) so that I can replot a number of charts depending on the selection (i.e. changing xaxis.min and xaxis.max) and keep them all correlated.
I have no idea where to begin with regards to modifying the example, any suggestions or hints would be most welcome.
Thanks
Update modified code from zoom example help still required!
enter code here
$(function () {
var plots = [];
var placeholders = $(".flot");
var d1 = [];
for (var i = 0; i < Math.PI * 2; i += 0.25)
d1.push([i, Math.sin(i)]);
var data = [ d1 ];
var d2 = [];
for (var i = 0; i < Math.PI * 2; i += 0.25)
d2.push([i, Math.cos(i)]);
var data2 = [ d2 ];
var options = {
series: { lines: { show: true }, shadowSize: 0 },
/*xaxis: { zoomRange: [0.1, 10], panRange: [-10, 10] },
yaxis: { zoomRange: [0.1, 10], panRange: [-10, 10] },
zoom: {
interactive: true
},
pan: {
interactive: true
}*/
selection: { mode: "x"}
};
plots.push($.plot(placeholder, data, options));
plots.push($.plot(placeholder1, data2, options));
placeholders.bind("plotselected", function (event, plot) {
var axes = plot.getAxes();
for(var i=0; i< plots.length; i++) {
plots[i].getOptions().xaxes.min = axes.xaxis.from;
plots[i].getOptions().xaxes.max = axes.xaxis.to;
//plots[i].getOptions().yaxes[0].min = axes.yaxis.min;
//plots[i].getOptions().yaxes[0].max = axes.yaxis.max;
plots[i].setupGrid();
plots[i].draw();
plots[i].clearSelection();
}
});
});

Related

creating tickboxes to show/hide series in flot

I am trying to show/hide each line in graph using checkbox. The check box appears but nothing happens when i click it
https://jsfiddle.net/shorif2000/2pb3yu5t/
var arrayFromPHP = {
chartdata: {
"2G": "[[1450623600000,99.55],[1450620000000,99.54],[1450616400000,99.51],[1450612800000,99.51],[1450609200000,99.5],[1450605600000,99.51],[1450602000000,99.52],[1450598400000,99.55],[1450594800000,99.57],[1450591200000,99.57],[1450587600000,99.58],[1450584000000,99.58],[1450580400000,99.58],[1450576800000,99.57],[1450573200000,99.54],[1450569600000,99.54],[1450566000000,99.53],[1450562400000,99.5],[1450558800000,99.49],[1450555200000,99.5],[1450551600000,99.54],[1450548000000,99.6],[1450544400000,99.6],[1450540800000,99.6],[1450537200000,99.57],[1450533600000,99.52],[1450530000000,99.55],[1450526400000,99.56],[1450522800000,99.54],[1450519200000,99.52],[1450515600000,99.55],[1450512000000,99.59],[1450508400000,99.57],[1450504800000,99.57],[1450501200000,99.57],[1450497600000,99.59],[1450494000000,99.59],[1450490400000,99.58],[1450486800000,99.58],[1450483200000,99.57],[1450479600000,99.58],[1450476000000,99.58],[1450472400000,99.57],[1450468800000,99.57],[1450465200000,99.56],[1450461600000,99.56],[1450458000000,99.55],[1450454400000,99.49],[1450450800000,99.39],[1450447200000,99.36],[1450443600000,99.28],[1450440000000,99.23],[1450436400000,99.17],[1450432800000,99.18],[1450429200000,99.28],[1450425600000,99.34],[1450422000000,99.44],[1450418400000,99.44],[1450414800000,99.46],[1450411200000,99.45],[1450407600000,99.45],[1450404000000,99.35],[1450400400000,99.36],[1450396800000,99.35]]",
"2G3G": "[[1450623600000,99.53],[1450620000000,99.52],[1450616400000,99.5],[1450612800000,99.49],[1450609200000,99.5],[1450605600000,99.5],[1450602000000,99.51],[1450598400000,99.55],[1450594800000,99.58],[1450591200000,99.58],[1450587600000,99.58],[1450584000000,99.59],[1450580400000,99.58],[1450576800000,99.58],[1450573200000,99.57],[1450569600000,99.57],[1450566000000,99.54],[1450562400000,99.56],[1450558800000,99.58],[1450555200000,99.57],[1450551600000,99.59],[1450548000000,99.62],[1450544400000,99.61],[1450540800000,99.58],[1450537200000,99.57],[1450533600000,99.52],[1450530000000,99.53],[1450526400000,99.53],[1450522800000,99.49],[1450519200000,99.5],[1450515600000,99.53],[1450512000000,99.6],[1450508400000,99.6],[1450504800000,99.6],[1450501200000,99.6],[1450497600000,99.6],[1450494000000,99.6],[1450490400000,99.6],[1450486800000,99.6],[1450483200000,99.6],[1450479600000,99.6],[1450476000000,99.6],[1450472400000,99.58],[1450468800000,99.56],[1450465200000,99.57],[1450461600000,99.56],[1450458000000,99.56],[1450454400000,99.48],[1450450800000,99.38],[1450447200000,99.3],[1450443600000,99.25],[1450440000000,99.16],[1450436400000,99.03],[1450432800000,99.04],[1450429200000,99.14],[1450425600000,99.23],[1450422000000,99.36],[1450418400000,99.37],[1450414800000,99.38],[1450411200000,99.37],[1450407600000,99.37],[1450404000000,99.34],[1450400400000,99.34],[1450396800000,99.34]]",
"2G3G4G": "[[1450623600000,99.57],[1450620000000,99.56],[1450616400000,99.55],[1450612800000,99.54],[1450609200000,99.56],[1450605600000,99.55],[1450602000000,99.56],[1450598400000,99.59],[1450594800000,99.62],[1450591200000,99.62],[1450587600000,99.63],[1450584000000,99.63],[1450580400000,99.62],[1450576800000,99.62],[1450573200000,99.62],[1450569600000,99.62],[1450566000000,99.59],[1450562400000,99.61],[1450558800000,99.62],[1450555200000,99.62],[1450551600000,99.63],[1450548000000,99.65],[1450544400000,99.65],[1450540800000,99.62],[1450537200000,99.61],[1450533600000,99.55],[1450530000000,99.56],[1450526400000,99.56],[1450522800000,99.53],[1450519200000,99.54],[1450515600000,99.57],[1450512000000,99.64],[1450508400000,99.64],[1450504800000,99.64],[1450501200000,99.64],[1450497600000,99.64],[1450494000000,99.64],[1450490400000,99.63],[1450486800000,99.63],[1450483200000,99.63],[1450479600000,99.62],[1450476000000,99.62],[1450472400000,99.61],[1450468800000,99.6],[1450465200000,99.6],[1450461600000,99.59],[1450458000000,99.59],[1450454400000,99.52],[1450450800000,99.43],[1450447200000,99.34],[1450443600000,99.3],[1450440000000,99.2],[1450436400000,99.07],[1450432800000,99.08],[1450429200000,99.19],[1450425600000,99.28],[1450422000000,99.41],[1450418400000,99.42],[1450414800000,99.42],[1450411200000,99.42],[1450407600000,99.42],[1450404000000,99.39],[1450400400000,99.39],[1450396800000,99.4]]",
"3G": "[[1450623600000,99.51],[1450620000000,99.52],[1450616400000,99.5],[1450612800000,99.48],[1450609200000,99.5],[1450605600000,99.49],[1450602000000,99.5],[1450598400000,99.54],[1450594800000,99.58],[1450591200000,99.58],[1450587600000,99.58],[1450584000000,99.59],[1450580400000,99.57],[1450576800000,99.58],[1450573200000,99.58],[1450569600000,99.58],[1450566000000,99.55],[1450562400000,99.58],[1450558800000,99.61],[1450555200000,99.6],[1450551600000,99.61],[1450548000000,99.63],[1450544400000,99.61],[1450540800000,99.58],[1450537200000,99.57],[1450533600000,99.51],[1450530000000,99.52],[1450526400000,99.52],[1450522800000,99.48],[1450519200000,99.49],[1450515600000,99.52],[1450512000000,99.6],[1450508400000,99.62],[1450504800000,99.62],[1450501200000,99.61],[1450497600000,99.61],[1450494000000,99.61],[1450490400000,99.61],[1450486800000,99.61],[1450483200000,99.61],[1450479600000,99.6],[1450476000000,99.6],[1450472400000,99.58],[1450468800000,99.56],[1450465200000,99.57],[1450461600000,99.56],[1450458000000,99.56],[1450454400000,99.48],[1450450800000,99.38],[1450447200000,99.28],[1450443600000,99.24],[1450440000000,99.13],[1450436400000,98.98],[1450432800000,98.99],[1450429200000,99.09],[1450425600000,99.19],[1450422000000,99.33],[1450418400000,99.34],[1450414800000,99.35],[1450411200000,99.34],[1450407600000,99.34],[1450404000000,99.33],[1450400400000,99.33],[1450396800000,99.33]]",
"4G": "[[1450623600000,99.89],[1450620000000,99.84],[1450616400000,99.87],[1450612800000,99.89],[1450609200000,99.95],[1450605600000,99.95],[1450602000000,99.96],[1450598400000,99.95],[1450594800000,99.97],[1450591200000,99.96],[1450587600000,99.96],[1450584000000,99.97],[1450580400000,99.97],[1450576800000,99.98],[1450573200000,99.99],[1450569600000,99.98],[1450566000000,99.97],[1450562400000,99.97],[1450558800000,99.96],[1450555200000,99.96],[1450551600000,99.95],[1450548000000,99.92],[1450544400000,99.92],[1450540800000,99.9],[1450537200000,99.89],[1450533600000,99.84],[1450530000000,99.81],[1450526400000,99.84],[1450522800000,99.82],[1450519200000,99.81],[1450515600000,99.83],[1450512000000,99.93],[1450508400000,99.92],[1450504800000,99.92],[1450501200000,99.92],[1450497600000,99.92],[1450494000000,99.92],[1450490400000,99.85],[1450486800000,99.86],[1450483200000,99.86],[1450479600000,99.84],[1450476000000,99.83],[1450472400000,99.88],[1450468800000,99.85],[1450465200000,99.84],[1450461600000,99.84],[1450458000000,99.84],[1450454400000,99.8],[1450450800000,99.75],[1450447200000,99.67],[1450443600000,99.64],[1450440000000,99.51],[1450436400000,99.34],[1450432800000,99.36],[1450429200000,99.56],[1450425600000,99.69],[1450422000000,99.79],[1450418400000,99.78],[1450414800000,99.78],[1450411200000,99.77],[1450407600000,99.78],[1450404000000,99.77],[1450400400000,99.79],[1450396800000,99.87]]"
},
data_rows: 64,
days: "3",
days_per_period: 1,
days_per_period_unavailability: 1,
filter: "",
filtersql: "",
filtersql2: " AND crqcoos is null AND crqinflight is null",
hours: 1,
hours_unavailability: 1,
max_days: 30,
min: 98,
period: "hh24",
tick: 6,
tick_type: "hour",
timeformat: "%a<br>%H:%M %p<br>%d %b"
};
var datasets = {};
choices_CAGraph(arrayFromPHP);
function choices_CAGraph(arrayFromPHP) { //initial load
//var datasets = [];
$.each(arrayFromPHP.chartdata, function(i, elem) {
var jsonObj = $.parseJSON('[' + elem + ']');
var iLabel = i;
datasets[i.toLowerCase()] = {
label: iLabel,
data: jsonObj[0]
};
});
var i = 0;
$.each(datasets, function(key, val) {
val.color = i;
++i;
});
var choiceContainer_CAGraph = $("#choices_CAGraph");
$.each(datasets, function(key, val) {
choiceContainer_CAGraph.append("<input class='cc' type='checkbox' name='" + key + "' checked='checked' id='id" + key + "' value='" + key + "'></input>" + "<label for='id" + key + "'>" + val.label + "</label>");
});
//choiceContainer_CAGraph.find("input").click(plotAccordingToChoices());
var showpoints = false;
if ((arrayFromPHP.data_rows == 1) && (arrayFromPHP.period == "hh24") && (arrayFromPHP.days == 1))
showpoints = true;
else if ((arrayFromPHP.period == "dd" || arrayFromPHP.period == "day" || arrayFromPHP.period == "mon") && arrayFromPHP.days == 1)
showpoints = true;
var options = {
legend: {
position: "sw",
noColumns: 5
},
yaxis: {
min: arrayFromPHP.min,
max: 100
},
xaxis: {
mode: "time",
timeformat: arrayFromPHP.timeformat,
tickSize: [arrayFromPHP.tick, arrayFromPHP.tick_type]
},
grid: {
clickable: true,
hoverable: true
},
series: {
points: {
show: showpoints
}
}
};
plotAccordingToChoices(options);
}
function plotAccordingToChoices(options) {
var choiceContainer_CAGraph = $("#choices_CAGraph");
var data = [];
choiceContainer_CAGraph.find("input:checked").each(function() {
var key = $(this).attr("name");
if (key && datasets[key])
data.push(datasets[key]);
});
$.plot("#CAGraph", data, options);
}
arrayFromPHP is json output from ajax request. I have 5 graphs on this page and I am trying to reuse this function so they all have show/hide feature. currently the tick boxes appear but nothing happens.
Change
//choiceContainer_CAGraph.find("input").click(plotAccordingToChoices());
to
choiceContainer_CAGraph.find("input").click(function() { plotAccordingToChoices(options); });
Updated fiddle

Immovable property not working in phaser

Here is my code regarding tilesprite & a player, player should not pass through tilesprite but unfortunately my code is not working, i tried all ways but couldn't achieve the desired result, i don't know why tile2.body.immovable=true; flag not working.
var main = {
preload: function () {
this.game.load.image('ground', 'images/ground.jpg');
this.game.load.image('bg', 'images/gabg.jpg');
this.game.load.atlasJSONHash('mover', 'images/sheet.png', 'images/sprites.json');
},
create: function () {
this.game.physics.startSystem(Phaser.Physics.ARCADE);
cursor = this.game.input.keyboard.createCursorKeys();
tile1 = game.add.tileSprite(0, 10, 1365, 582, 'bg');
tile2 = game.add.tileSprite(0, 590, 1366, 91, 'ground');
player = this.game.add.sprite(50, 550, 'mover');
player.frame = 0;
player.anchor.setTo(0.5, 0.5);
while (i < 11) {
anim.push(i)
i++;
}
player.animations.add('move', anim, 20, true);
this.game.physics.arcade.enable(player);
this.game.physics.arcade.enable(tile2);
tile2.body.immovable = true;
tile2.body.allowGravity = false;
player.body.gravity.y = 800;
},
update: function () {
this.game.physics.arcade.collide(tile2, player);
tile1.tilePosition.x += -1;
tile2.tilePosition.x += -4;
this.move();
},
move: function () {
if (cursor.up.isDown) {
player.body.velocity.y = -200;
player.animations.stop('move');
}
else {
player.animations.play('move');
}
},
};
var game = new Phaser.Game(1366, 768, Phaser.CANVAS, 'gamediv');
this.game.state.add('main', main);
this.game.state.start('main');
var anim = [];
var cursor, i=0;
var tile1;
var tile2;
var player;
Which version of Phaser are you using? Could it be related to this bug in 2.3.0?
http://www.html5gamedevs.com/topic/13856-problem-with-collide-method-from-physics-arcade-in-different-versions-of-phaser/?p=79032

Atlasboard and Rickshaw

Am trying to create a dashboard using the atlasboard and rickshaw. Now what happens is that atlasboard by default has a black background and am unable to see the x-axis and y-axis when creating graphs (e.g WIKI markdown analytics).
If you look at the example screenshot below from the official atlasboard page it appears that atlassian devs have managed to show the x-axis as a white color, so am wondering :
how they did it as am unable to achieve this with rickshaw
apparently.
Is there any css that needs to be overridden or is there any kind of
settings to be setup in nodejs?
or even another graph framework should be used other than rickshaw
(and how to do that)?
Can someone please explain?
(source: bitbucket.org)
Update1 following answer:
I have added the following within the widget:
widget = {
//runs when we receive data from the job
onData: function(el, data) {
function drawDashLine(val, max, min) {
var container = $('.content', el),
viewport = {
height: container.height(),
width: container.width()
},
topPosition = (viewport.height - Math.ceil(val/max * viewport.height));
var dashedLineLine = $('<hr />')
.attr('class', 'dashedLine')
.css({
width: el.width() - 40,
top: topPosition + 'px'
});
var lineLabel = $("<span />")
.attr('class', 'lineLabel')
.css({
top: topPosition + 'px'
})
.text(val);
container.append(dashedLineLine, lineLabel);
}
function paintMinMax(series) {
var mergedData = [];
for (var i = series.length - 1; i >= 0; i--) {
mergedData = mergedData.concat(series[i].data);
};
var min = _.min(_.pluck(mergedData, 'y')),
max = _.max(_.pluck(mergedData, 'y')),
mid = Math.round(max / 2);
drawDashLine(min, max, min);
drawDashLine(mid, max, min);
drawDashLine(max, max, min);
}
function paintTimeMark(chartWidth, chartHeight, startDate, endDate, timeMarkData) {
if (!timeMarkData || !timeMarkData.length) {
return;
}
var chartLengthMs = endDate - startDate;
for (var i = 0, l = timeMarkData.length; i < l; i++) {
var timeMark = timeMarkData[i];
// are we in the boundaries?
if ((timeMark.epoch < startDate) || (timeMark.epoch > endDate)){
continue; // out of boundaries
}
var lengthOfTimemark = timeMark.epoch - startDate;
var percentage = (lengthOfTimemark / chartLengthMs);
var leftPosition = Math.round(chartWidth * percentage);
var top = timeMark.top || 0;
var markHeight = chartHeight - top;
var color = timeMark.color || 'orange';
var mark = $('<div>')
.attr('class', 'mark')
.css({
left: leftPosition + 'px',
'margin-top': top + 'px',
'background-color' : color,
'height': markHeight + 'px'
});
var legend = $('<span>')
.attr('class', 'legend')
.css({
left: leftPosition + 'px',
'margin-top': top + 'px',
'height': markHeight + 'px'
})
.hide()
.text(timeMark.name);
$('.content', el).append(mark, legend);
var center = Math.round((legend.width() - (mark.width())) / 2);
// center align legend
(function(legend, center){
setTimeout(function(){
legend.css({
'margin-left': (-(center)) + 'px'
}).fadeIn();
},600);
}(legend, center));
}
}
function paintChart(width, height, series, color) {
for (var i = series.length - 1; i >= 0; i--) {
series[i].data = formatData(series[i].data);
}
var graph = new Rickshaw.Graph({
element: $('.graph', el)[0],
width: width,
height: height,
renderer: 'line',
offset: 'expand',
series: series
});
var xAxis = new Rickshaw.Graph.Axis.Time({
graph: graph,
timeFixture: new Rickshaw.Fixtures.Time.Local()
});
xAxis.render();
graph.render();
}
function formatData(rawData) {
var sortedData = _.sortBy(rawData, function(num) {
return num.date;
});
return _.map(sortedData, function(e) {
var ret = {
x: e.date,
y: e.sum
};
return ret;
});
}
var init = function() {
if (data.title) {
$('h2', el).text(data.title);
}
if ($('.graph', el).hasClass('rickshaw_graph')) {
$('.graph', el).empty();
}
if (!data.series.length) {
console.error('There is no results to paint the chart');
return;
}
// paint chart
var width = el.width() - 50;
var height = el.closest('li').height() - 80;
paintChart(width, height, data.series, data.color || 'yellow');
// paint min max dash
paintMinMax(data.series);
// paint time marks
paintTimeMark(width, height, data.startDate, data.endDate, data.timeMarks);
}();
}
};
Then within the job I have added :
module.exports = function(config, dependencies, job_callback) {
var text = "Hello World!";
var date1 = (new Date(2014, 4, 2, 1, 30, 0, 0))/1000;
var date2 = (new Date(2014, 5, 3, 2, 30, 0, 0))/1000;
var date3 = (new Date(2014, 6, 4, 3, 30, 0, 0))/1000;
var date4 = (new Date(2014, 7, 6, 4, 30, 0, 0))/1000;
var totalBacklogDefects = [{ x: date1, y : 40},
{ x: date2, y : 30},
{ x: date3, y : 23}, { x: date4, y : 10} ] ;
var blockedDefects = [{ x: date1, y : 32}, { x: date2, y : 22},
{ x: date3, y : 3}, { x: date4, y : 5} ] ;
var fixedDefects = [{ x: date1, y : 2}, { x: date2, y : 12},
{ x: date3 ,y : 20}, { x: date4, y : 25} ] ;
var series = [
{
data: totalBacklogDefects ,
color: 'steelblue',
name: 'Total Backlog'
},
{
data: blockedDefects ,
color: 'red',
name: 'Blocked Defects'
},
{
data: fixedDefects,
color: 'green',
name: 'Fixed'
}
];
var timeMarks = [
{
top: 0,
color: 'red',
name: 'test2',
epoch: date2
},
{
top: 0,
color: 'blue',
name: 'test1',
epoch: date3
}
];
job_callback(null, {title: "Graph Sandbox", series: series, startDate: date1 ,
endDate : date4 , timeMarks: timeMarks});
};
As for my html page I have added the following:
<h2>graphsandbox</h2>
<div class="content">
<div class="graph rickshaw_graph"></div>
</div>
Can you please let me know whether am using the right approach as currently this renders only an empty box?
The chart widget that you see in the screenshot belongs to an internal package, but I can share it with you :)
Rickshaw is available globally anyway, so you can create your own chart widgets check Rickshaw's examples.
widget = {
onData: function(el, data) {
function drawDashLine(val, max, min) {
var container = $('.content', el),
viewport = {
height: container.height(),
width: container.width()
},
topPosition = (viewport.height - Math.ceil(val/max * viewport.height));
var dashedLineLine = $('<hr />')
.attr('class', 'dashedLine')
.css({
width: el.width() - 40,
top: topPosition + 'px'
});
var lineLabel = $("<span />")
.attr('class', 'lineLabel')
.css({
top: topPosition + 'px'
})
.text(val);
container.append(dashedLineLine, lineLabel);
}
function paintMinMax(series) {
var mergedData = [];
for (var i = series.length - 1; i >= 0; i--) {
mergedData = mergedData.concat(series[i].data);
};
var min = _.min(_.pluck(mergedData, 'y')),
max = _.max(_.pluck(mergedData, 'y')),
mid = Math.round(max / 2);
drawDashLine(min, max, min);
drawDashLine(mid, max, min);
drawDashLine(max, max, min);
}
function paintTimeMark(chartWidth, chartHeight, startDate, endDate, timeMarkData) {
if (!timeMarkData || !timeMarkData.length) {
return;
}
var chartLengthMs = endDate - startDate;
for (var i = 0, l = timeMarkData.length; i < l; i++) {
var timeMark = timeMarkData[i];
// are we in the boundaries?
if ((timeMark.epoch < startDate) || (timeMark.epoch > endDate)){
continue; // out of boundaries
}
var lengthOfTimemark = timeMark.epoch - startDate;
var percentage = (lengthOfTimemark / chartLengthMs);
var leftPosition = Math.round(chartWidth * percentage);
var top = timeMark.top || 0;
var markHeight = chartHeight - top;
var color = timeMark.color || 'orange';
var mark = $('<div>')
.attr('class', 'mark')
.css({
left: leftPosition + 'px',
'margin-top': top + 'px',
'background-color' : color,
'height': markHeight + 'px'
});
var legend = $('<span>')
.attr('class', 'legend')
.css({
left: leftPosition + 'px',
'margin-top': top + 'px',
'height': markHeight + 'px'
})
.hide()
.text(timeMark.name);
$('.content', el).append(mark, legend);
var center = Math.round((legend.width() - (mark.width())) / 2);
// center align legend
(function(legend, center){
setTimeout(function(){
legend.css({
'margin-left': (-(center)) + 'px'
}).fadeIn();
},600);
}(legend, center));
}
}
function paintChart(width, height, series, color) {
for (var i = series.length - 1; i >= 0; i--) {
series[i].data = formatData(series[i].data);
}
var graph = new Rickshaw.Graph({
element: $('.graph', el)[0],
width: width,
height: height,
renderer: 'line',
offset: 'expand',
series: series
});
var xAxis = new Rickshaw.Graph.Axis.Time({
graph: graph,
timeFixture: new Rickshaw.Fixtures.Time.Local()
});
xAxis.render();
graph.render();
}
function formatData(rawData) {
var sortedData = _.sortBy(rawData, function(num) {
return num.date;
});
return _.map(sortedData, function(e) {
var ret = {
x: e.date,
y: e.sum
};
return ret;
});
}
var init = function() {
if (data.title) {
$('h2', el).text(data.title);
}
if ($('.graph', el).hasClass('rickshaw_graph')) {
$('.graph', el).empty();
}
if (!data.series.length) {
console.error('There is no results to paint the chart');
return;
}
// paint chart
var width = el.width() - 50;
var height = el.closest('li').height() - 80;
paintChart(width, height, data.series, data.color || 'yellow');
// paint min max dash
paintMinMax(data.series);
// paint time marks
paintTimeMark(width, height, data.startDate, data.endDate, data.timeMarks);
}();
}
};

nearest neighbour in dataset Node.js

I have a Node.js application that saves data to MongoDB.
Given one document, I want to find the most similar document in the database.
My idea is to implement some sort of nearest neighbour algorithm that takes all the records as a training sequence and returns the most similar document (including some sort of percentage on how similar these two documents are.)
E.g. having these records in my database...
{ name: "Bill", age: 10, pc: "Mac", ip: "68.23.13.8" }
{ name: "Alice", age: 22, pc: "Windows", ip: "193.186.11.3" }
{ name: "Bob", age: 12, pc: "Windows", ip: "56.89.22.1" }
...I want to find the closest document to this one
{ name: "Tom", age: 10, pc: "Mac", ip: "68.23.13.10" }
// algorithm returns "Bill", .76
Are there any Node modules/implementations that take any kind of objects/parameters and return their nearest neighbour?
Here is some example code. It assumes that you can run the search on every request. If you want to modify it, make sure that all similarity functions return a number between 0 and 1.
function tokenize(string) {
var tokens = [];
for (var i = 0; i < string.length-1; i++) {
tokens.push(string.substr(i,2));
}
return tokens.sort();
}
function intersect(a, b)
{
var ai=0, bi=0;
var result = new Array();
while( ai < a.length && bi < b.length )
{
if (a[ai] < b[bi] ){ ai++; }
else if (a[ai] > b[bi] ){ bi++; }
else /* they're equal */
{
result.push(a[ai]);
ai++;
bi++;
}
}
return result;
}
function sum(items) {
var sum = 0;
for (var i = 0; i < items.length; i++) {
sum += items[i];
}
return sum;
}
function wordSimilarity(a, b) {
var left = tokenize(a);
var right = tokenize(b);
var middle = intersect(left, right);
return (2*middle.length) / (left.length + right.length);
}
function ipSimilarity(a, b) {
var left = a.split('.');
var right = b.split('.');
var diffs = [];
for (var i = 0; i < 4; i++) {
var diff1 = 255-left[i];
var diff2 = 255-right[i];
var diff = Math.abs(diff2-diff1);
diffs[i] = diff;
}
var distance = sum(diffs)/(255*4);
return 1 - distance;
}
function ageSimilarity(a, b) {
var maxAge = 100;
var diff1 = maxAge-a;
var diff2 = maxAge-b;
var diff = Math.abs(diff2-diff1);
var distance = diff / maxAge;
return 1-distance;
}
function recordSimilarity(a, b) {
var fields = [
{name:'name', measure:wordSimilarity},
{name:'age', measure:ageSimilarity},
{name:'pc', measure:wordSimilarity},
{name:'ip', measure:ipSimilarity}
];
var sum = 0;
for (var i = 0; i < fields.length; i++) {
var field = fields[i];
var name = field.name;
var measure = field.measure;
var sim = measure(a[name], b[name]);
sum += sim;
}
return sum / fields.length;
}
function findMostSimilar(items, query) {
var maxSim = 0;
var result = null;
for (var i = 0; i < items.length; i++) {
var item = items[i];
var sim = recordSimilarity(item, query);
if (sim > maxSim) {
maxSim = sim;
result = item;
}
}
return result
}
var items = [
{ name: "Bill", age: 10, pc: "Mac", ip: "68.23.13.8" },
{ name: "Alice", age: 22, pc: "Windows", ip: "193.186.11.3" },
{ name: "Bob", age: 12, pc: "Windows", ip: "56.89.22.1" }
];
var query = { name: "Tom", age: 10, pc: "Mac", ip: "68.23.13.10" };
var result = findMostSimilar(items, query);
console.log(result);
A straightforward way of doing this would be to calculate a diff between the two documents and the larger the diff, the larger the distance. You could normalize the diff using the maximum possible diff which should give you relative distances that you can compare against each other.
Take a look at this question for calculating a diff on json documents.
Delta encoding for JSON objects

Flot tooltip hovering

I have an issue with the tooltip in flot. I'm passing a timestamp data and it will reflect as a number, e.g 1113340002003. What I want to do is that when I hover over a data point, it will reflect as the date: 01/04/2012 and not that number. Any help would be great! Stuck here for a few hours....
This is what I'm passing to plot:
var time = (new Date(dates[i]));
graph.push([time, demand[i]]);
This is the section that I used to plot my graph:
var options = {
series: {
lines: { show: true },
points: { show: true }
},
grid: { hoverable: true, clickable: true },
yaxis: { min: 0, max: 20000 },
xaxis: {
mode: "time", timeformat: "%d/%m/%y"}
var plot = $.plot($("#placeholder"),
[ { data: graph, label: "price" } ],
options);
function showTooltip(x, y, contents) {
$('<div id="tooltip">' + contents + '</div>').css( {
position: 'absolute',
display: 'none',
top: y + 5,
left: x + 5,
border: '1px solid #fdd',
padding: '2px',
'background-color': '#fee',
opacity: 0.80
}).appendTo("body").fadeIn(200);
}
var previousPoint = null;
$("#placeholder").bind("plothover", function (event, pos, item) {
$("#x").text(pos.x.toFixed(2));
$("#y").text(pos.y.toFixed(2));
if($("#enableTooltip:checked").length > 0) {
if (item) {
if (previousPoint != item.dataIndex) {
previousPoint = item.dataIndex;
$("#tooltip").remove();
var x = item.datapoint[0].toFixed(2),
y = item.datapoint[1].toFixed(2);
var td = x.split("/");
Just convert it to a javascript data object and then build the string yourself.
var d = new Date(item.datapoint[0]);
var someDay = d.getDate();
var someMonth = d.getMonth() + 1; //months are zero based
var someYear = d.getFullYear();
var stringDate = someMonth + "/" + someDay + "/" + someYear;
var x = stringDate;
var y = item.datapoint[1].toFixed(2);

Resources