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;
}
}
Related
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.
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/
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.
I have created a single page for all my reports and I am loading different versions of those reports (line, pie, chart, graph, etc) with a toolbar I made. All is working well there, except on the non-table type charts (line,pie,bar,etc). When those get rendered, I found that the text in the legends and series become blurry and through some research here and other places found that they are converted to images, which are then getting resized on me though a css class that is auto generated.
Firstly, what i'm trying to do:
I want to remove this class from the image that is generated at the time it is loaded. If i turn off async rendering on my report
AsyncRendering="false"
Along with this bit of jquery (targeting the div that contains the reportviewer):
$(document).ready(function () {
$('#reportDiv img').removeAttr('class');
});
Then the result is as expected. The image is not scaled and all is well. The problem, however, is that some of these reports may be quite large, resulting in the user not having any visual feedback of whether or not something is happening. I would like to continue using async rendering, so I started to look at the reportviewer javascript api.
Sys.Application.add_load(function () {
var reportViewer = $find("ctl00_mainContentPlaceHolder_ReportViewer1");
reportViewer.add_propertyChanged(viewerPropertyChanged);
});
function viewerPropertyChanged(sender, e) {
var viewer = $find("ctl00_mainContentPlaceHolder_ReportViewer1");
if (e.get_propertyName() === "isLoading") {
var button = document.getElementById("ctl00_mainContentPlaceHolder_ctlReportParamModuleId1_btnRunReport");
button.disabled = viewer.get_isLoading();
}
else {
if ($find("ctl00_mainContentPlaceHolder_ReportViewer1").get_reportAreaContent() == Microsoft.Reporting.WebFormsClient.ReportAreaContent.ReportPage) {
alert("here");
}
}
}
The first portion (isLoading) works as expected disabling the button. However immediately upon load I get
Object doesn't support property or method 'get_reportAreaContent'
Am I missing something obvious? These are the links from msdn that I used for reference:
reportviewer isLoading
reportviewer ReportAreaContentType
Bar graphs, Line graphs, pie charts, etc. are rendered as images. The images get re-sized based on the size of the report viewer control. Instead of using AsyncRendering="false", I created this javascript workaround and it has solved my problem.
var app = Sys.Application;
app.add_init(ApplicationInit);
function ApplicationInit(sender) {
var prm = Sys.WebForms.PageRequestManager.getInstance();
if (!prm.get_isInAsyncPostBack()) {
prm.add_endRequest(EndRequest)
}
}
function EndRequest(sender, args) {
var reportViewerControlId = "ReportViewer1";
if (sender._postBackControlClientIDs[0].indexOf(reportViewerControlId) >= 0) {
var reportViewerControlContainer = "reportViewerContainer"; // Id of <DIV>
var renderedReportImage = $("#" + reportViewerControlContainer + " img");
renderedReportImage.removeAttr("style").removeAttr("class");
var styleAttr = renderedReportImage.attr("style");
var classAttr = renderedReportImage.attr("class");
if (typeof styleAttr === 'undefined') {
console.log("Successfully removed the style attribute from the rendered report image!");
}
if (typeof classAttr === 'undefined') {
console.log("Successfully removed the class attribute from the rendered report image!");
}
}
}
Basically, I am listening to the endRequest of the PageRequestManager for my ReportViewerControl's ID, then simply removing the style and class attributes from the image to display it unmodified.
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);
}
});