heat map hide columns by date - javascript

Iv set up a custom onClick event to hide columns in a heat map using a "fake group". Everything works, as long as you wait till the animations finish before clicking on another year form the yearSlicer. Clicking before it is finished loading will cause unwanted results.
fake group:
function filter_bins(source_group, f) {
return {
all: function () {
return source_group.all().filter(function (d) {
return f(d.value);
});
}
};
}
var filtered_years_group = filter_bins(FTEMonthGroup, function (d) {
return yearsHidden.length == 0 ? !yearsHidden.includes(d.Year) : yearsHidden.includes(d.Year);
});
yearSlicer onClick:
yearSlicer.on("renderlet.somename", function (chart) {
chart.selectAll('rect').on("click", function (d) {
hideYear(d.key);
return chart._onClick(d)
});
});
setting years to hide:
var yearsHidden = [];
var hideYear = function (year) {
var index = yearsHidden.indexOf(year);
if (index === -1) {
yearsHidden.push(year);
} else {
yearsHidden.splice(index, 1);
}
heatMap.redraw();
}
https://jsfiddle.net/_M_M_/fLvugo3h/

I observed that sometimes the heatmap data would not be filtered by the current selection of years, causing grey cells to appear. Hope this is the same problem you are describing!
I'm not sure what is going wrong here, but I noticed that yearsHidden is the same as yearSlicer.filters() - you are duplicating the toggle behavior that the chart already has.
It's always nice to fix a bug by deleting code. I found that I could fix it by changing the filter_bins function to
var filtered_years_group = filter_bins(FTEMonthGroup, function(d) {
var years = yearSlicer.filters();
return years.length ? years.includes(d.Year) : true;
});
and then remove everything to do with yearsHidden, as well as the click event handler.
https://jsfiddle.net/gordonwoodhull/8npv5zhq/19/
Without going into a deep debugging session, my guess is that they got out of synch because a lot of the click handlers for dc.js are asynchronous. So your handler could get called at different times from the dc.js filtering code.

Related

Vis.js: Adding showPopup in react style

I have a question to ask regarding vis.js popup option. Currently I am trying to implement it in react style so I was using https://github.com/crubier/react-graph-vis/tree/master/example as a starting point.
I realized that in src\index.js file I can add events array since I realize the select option is in there. However, when I do the following:
const events = {
select: function(event) {
var { nodes, edges } = event;
console.log("Selected nodes:");
console.log(nodes);
console.log("Selected edges:");
console.log(edges);
},
showPopup: function(event) {
document.getElementById('root').innerHTML = '<h2>showPopup event</h2>'+ JSON.stringify(params, null, 4);
}
};
I am not able to trigger the popup even at all. Inside the lib\index.js, I noticed that the code is supposed to loop over the events array:
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = Object.keys(events)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var _eventName = _step2.value;
this.Network.on(_eventName, events[_eventName]);
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
}
and I checked that vis.js has the popup option according to the documentation which can be found here: http://visjs.org/docs/network/
I am currently stuck on figuring out how to trigger the popup. There is a requirement to use react since the application will be based on it. It would be great if someone can point out what I did wrong.
Thanks in advance. XD
NOTE: This question is in regards to the github project that I am trying to build on top of. Therefore it is a little different because I am not taking a barebone vis.js
You are mixing things up. showPopup is an event, a function that is called when the popup is shown. You do not call it to show the popup.
To show the popup you simply hover over a node that has a title property.
Check out this fiddle I made (is in pure JS though): http://jsfiddle.net/56t9c0t4/

javascript slider change call function sequence

Following are some code for explaining
$("#upperBound").on('input',function(){
console.log("1");
loadAndViewImagethresholding(imageId);
console.log("4");
});
function loadAndViewImagethresholding(imageId) {
_("thresholdingdicomImage").style.opacity = "0.3";
var elementthresh = $('#thresholdingdicomImage').get(0);
cornerstone.enable(elementthresh);
var imageIdpro="wadouri:"+"http://localhost:8888/dicomread/temp/"+loadfileName;
cornerstone.loadImage(imageId).then(function(image) {
cornerstone.loadImage(imageIdpro).then(function(imagepro) {
upper=_("upperBound").value;
lower=_("lowerBound").value;
for (var i = 0;i<image.getPixelData().length;i++) {
if(imagepro.getPixelData()[i]<lower||imagepro.getPixelData()[i]>upper)
{
imagepro.getPixelData()[i]=image.minPixelValue;
// console.log("imageproCopyaftercompare:"+imagepro.getPixelData()[436512]);
}
else{imagepro.getPixelData()[i]=image.maxPixelValue;
//console.log("imageproCopyaftercompare:"+imagepro.getPixelData()[436512]);
}
}
console.log("imageproCopyaftercompare:"+imagepro.getPixelData()[436512]);
var viewportthresh = cornerstone.getDefaultViewportForImage(elementthresh, imagepro);
console.log("2");
cornerstone.displayImage(elementthresh, imagepro);
});
});
}
So the basic idea is to use slideupperBound to change upper (lower) value to threshold image, but it seems nothing changed after I change slider. I make a console.log for 436512th pixel, It seems after give slider input that imagepro(which is a raw data array) has been changed, but next displayImage is not implemented.
Then I make a console.log("1 to 4") to see how that slider event implement, the result is 1,4,2,3 rather than 1,2,3,4 as I expect.
So my question is how does this slider implement? Will it implements 1,4 first then calling the function(loadAndViewImagethresholding(imageId);) inside?
If possible, give me some idea to fix that problem, any help appreciated.
BTW, cornerstone is the js I used.

Dygraphs live trending and synchronization

There are two dygraphs on my page. On regular intervals new time series data is read from source and written to local variables, then the graphs are updated using the following commands:
vm.graph1.updateOptions(
{ 'file': customData1, 'labels': vm.labels1 });
vm.graph2.updateOptions(
{ 'file': customData2, 'labels': vm.labels2 });
This works perfectly fine, the graphs show the live trend as expected.
However, when I try to synchronize the two graphs by including the syncrhonizer.js and using the following command:
vm.graphSync = Dygraph.synchronize(vm.graph1, vm.graph2);
Then the "live updates" stop working. In other words, the graph doesn't display any of the new incoming values (it just displays the same static time span). The syncronization works fine, both for selection and zoom.
The data is still getting updated, but now I have to double click on the graph (or manually pan) in order to see the most recent data.
Anyone have any ideas or working solutions for syncrhronizing live trends using Dygraphs?
You'll need to explicitly set the dateWindow after updating the data:
vm.graph1.updateOptions(
{ 'file': customData1, 'labels': vm.labels1 });
vm.graph2.updateOptions(
{ 'file': customData2, 'labels': vm.labels2 });
vm.graph1.updateOptions({
dateWindow: vm.graph1.xAxisExtremes()
});
// ^^^ this will synchronize the other charts, too
The fact that you have to do this could be considered a bug with synchronizer. Feel free to file an issue against dygraphs, preferably with a link to a repro via dygraphs.com/fiddle.
This is a working solution, however, I had to change the "synchronizer.js" from dygraphs, so comments and other suggestions are still welcome.
In attachZoomHandlers function in synchronizer.js, I made this change:
for (var j = 0; j < gs.length; j++) {
if (gs[j] == me) continue;
//added if/else block, the original code was the stuff inside else block
if (me.ignoreXrangeSync && me.ignoreXrangeSync === true) {
//if ignoreXrangeSync flag is set, only update y-range
gs[j].updateOptions( {
valueRange: yrange
});
}
else {
//if ignoreXrangeSync flag is NOT set, run original code (so manual zoom works)
gs[j].updateOptions( {
dateWindow: range,
valueRange: yrange
});
}
}
And in my original code, I made this change.
vm.graph1.ignoreXrangeSync = vm.graph2.ignoreXrangeSync = true;
vm.graph1.updateOptions(
{ 'file': customData1, 'labels': vm.labels1 });
vm.graph2.updateOptions(
{ 'file': customData2, 'labels': vm.labels2 });
vm.graph1.ignoreXrangeSync = vm.graph2.ignoreXrangeSync = false;
Also, I needed to add these zoom callbacks (one for each graph) for my graph for live trending to start when double clicking after having manually zoomed in the graph(s).
var graph1ZoomCallback = function () { //callback for graph1
if(!vm.graph1.isZoomed()) { //if graph1 has been double clicked
vm.graph2.ignoreXrangeSync = true; //note, graph2
vm.graph2.resetZoom();
vm.graph2.ignoreXrangeSync = false;
}
}

How can I implement a callback after layer.destroyChilden()

This is for homework and it has an annoying bug I can't solve after hours of struggle.
http://canvaseu.chrisloughnane.net/
When on the eu map clicking a country path fires the bound event.
This event destroys the layers children and loads the country. BUT if you click and quickly move the mouse and let go the button the country loads but the country path from the eu remains. This is best demonstrated with Spain as shown in the screen grab.
I am hoping a callback after mapLayer.destroyChildren(); to then call the load function would solve my problem.
This can be a little difficult to replicate.
I'm sure my control is too tied up with my view but I haven't been able to see a solution to separate them neatly.
**** EDIT ****
I came up with a solution that works partially but I think this is terrible hack code, I added to the mousedown binding down = true; and added checks to the mouseout binding, please see below.
What I think is happening is when you move the mouse and let the button go very quickly the mouseover binding is over riding the mouseup.
This solution isn't ideal, after loading a number of countries and mouseover on regions the canvas response slows down.
Event Binding
path.on('mousedown touchstart', function()
{
down = true;
this.setStroke('red');
this.moveTo(topLayer);
/****
* Handle if the path we are displaying in canvas is the eu
* to allow selection and load of country path point data.
*/
if (associativeCountryArray[lastCountry].getText() == 'eu')
{
associativeCountryArray[lastCountry].setFill('#bbb');
associativeCountryArray[lastCountry].setFontStyle('normal');
countrynames[lastCountry].selected = false;
this.moveTo(mapLayer);
mapLayer.destroyChildren();
lastCountry = this.getName();
countrynames[this.getName()].selected = true;
associativeCountryArray[this.getName()].setFill("rgb(" + r + "," + g + "," + b + ")");
associativeCountryArray[this.getName()].setFontStyle('bold');
loadPaths(this.getName().replace(/\s/g, ''));
countryNameLayer.draw();
}
else
{
window.open('https://www.google.com/search?q=' + this.getName(),'_blank');
}
topLayer.drawScene();
});
path.on('mouseout', function()
{
if(!down)
{
document.body.style.cursor = 'default';
this.setFill('#eee');
this.setStrokeWidth(settings.strokewidthstart);
/****
* On hover, only change colour of country names around edge of canvas if we are on the 'eu' map
*/
if (lastCountry == 'eu')
{
associativeCountryArray[this.getName()].setFill('#bbb');
associativeCountryArray[this.getName()].setFontStyle('normal');
}
this.setStroke('#555');
this.moveTo(mapLayer);
writeMessage('');
topLayer.draw();
countryNameLayer.draw();
}
else
{
down = false;
}
});
path.on('mouseup touchend', function()
{
this.setStroke('black');
topLayer.drawScene();
down = false;
});
Like this:
Send the container (group,layer) you want to clear and the callback you want triggered.
function myDestroyChildren(container,callback) {
var children = container.getChildren();
while(children.length > 0) {
children[0].destroy();
}
callback();
}

Highcharts: "Print all" button

Is it possible to create a "Print all" button for Highcharts, where more that one chart is printed?
I know that exporting multiple charts is possible, as demonstrated in the jFiddle: http://jsfiddle.net/highcharts/gd7bB/1/ but I'm not sure Highcharts allows a similar method with printing.
The exportChart method accepts parameters so you can send it more than one chart. However, the print method does not accept any parameters. So, to print you have to specify each chart separately like chart1.print(); and chart2.print(); which prints them each separately.
There are two workarounds:
Printing the whole page without using Highcharts printing. Here is an example.
You could export both of the charts to a pdf file and then print form there. Here is an example on how to export multiple charts to one pdf.
Here is an alternative solution that does the printing directly. It's based on the chart.print() function, but it works on multiple charts.
See the demo here: http://jsfiddle.net/jim31415/q5Rzu/150/
//--------------------------------------------------------------------
$("#print").click(function () {
printCharts([chart1, chart2, chart3]);
});
//--------------------------------------------------------------------
function printCharts(charts) {
var origDisplay = [],
origParent = [],
body = document.body,
childNodes = body.childNodes;
// hide all body content
Highcharts.each(childNodes, function (node, i) {
if (node.nodeType === 1) {
origDisplay[i] = node.style.display;
node.style.display = "none";
}
});
// put the charts back in
$.each(charts, function (i, chart) {
origParent[i] = chart.container.parentNode;
body.appendChild(chart.container);
});
// print
window.print();
// allow the browser to prepare before reverting
setTimeout(function () {
// put the charts back in
$.each(charts, function (i, chart) {
origParent[i].appendChild(chart.container);
});
// restore all body content
Highcharts.each(childNodes, function (node, i) {
if (node.nodeType === 1) {
node.style.display = origDisplay[i];
}
});
}, 500);
}
});

Categories