I created a chart with the help of amCharts that shows temperature from the examples on their site. The chart displayed correctly.
Now I am getting the temperature from a database with C# and I am trying to pass the value to the function where temperature was hard coded, so I get dynamic values. However, I just get the chart and the needle is still at 0 and does not show the temperature.
I have tried 3 ways so far:
I used a hidden field, assigned the value to hidden field in C# and called the JavaScript function showing the charts. It only shows chart. The needle does not change.
I used script manager and Web API, (I don't know Web API and just used code on the Internet), it is the same result however the function in c# keeps on being continuously called.
I put the entire amCharts code in a JavaScript function . I got values in c# and then used
String script = "window.onload = function() { UpdateTemp('" + dt.Rows[0][0].ToString()+ "'); };";
ClientScript.RegisterStartupScript(this.GetType(), "UpdateTemp", script, true);
which again shows same result, map is shown needle stays on 0.
This is my code for the 3rd approach:
ASPX page, JavaScript function
<script>
function UpdateTemp(temp) {
am4core.ready(function() {
// Themes begin
am4core.useTheme(am4themes_animated);
// Themes end
// create chart
var chart = am4core.create("chartdiv", am4charts.GaugeChart);
chart.hiddenState.properties.opacity = 0; // this makes initial fade in effect
chart.innerRadius = -25;
var axis = chart.xAxes.push(new am4charts.ValueAxis());
axis.min = 0;
axis.max = 100;
axis.strictMinMax = true;
axis.renderer.grid.template.stroke = new am4core.InterfaceColorSet().getFor("background");
axis.renderer.grid.template.strokeOpacity = 0.3;
var colorSet = new am4core.ColorSet();
var range0 = axis.axisRanges.create();
range0.value = 0;
range0.endValue = 50;
range0.axisFill.fillOpacity = 1;
range0.axisFill.fill = colorSet.getIndex(0);
range0.axisFill.zIndex = - 1;
var range1 = axis.axisRanges.create();
range1.value = 50;
range1.endValue = 80;
range1.axisFill.fillOpacity = 1;
range1.axisFill.fill = colorSet.getIndex(2);
range1.axisFill.zIndex = -1;
var range2 = axis.axisRanges.create();
range2.value = 80;
range2.endValue = 100;
range2.axisFill.fillOpacity = 1;
range2.axisFill.fill = colorSet.getIndex(4);
range2.axisFill.zIndex = -1;
var hand = chart.hands.push(new am4charts.ClockHand());
// using chart.setTimeout method as the timeout will be disposed together with a chart
chart.setTimeout(randomValue, 2000);
function randomValue(temp) {
hand.showValue(temp, 1000, am4core.ease.cubicOut);
chart.setTimeout(randomValue, 2000);
}
}); // end am4core.ready()
};
</script>
C# function to get temperature and call JavaScript function
public void BindGrid(String charttype)
{
string constring = "Data Source=********.DOMAIN.ORG01;Initial Catalog=Temperature;Integrated Security=SSPI;";
using (SqlConnection con = new SqlConnection(constring))
{
using (SqlCommand cmd = new SqlCommand("SELECT Temperature,HighestPoint,LowestPoint FROM Temperature", con))
{
cmd.CommandType = CommandType.Text;
con.Open();
DataTable dt = new DataTable();
dt.Load(cmd.ExecuteReader());
temp1.Value = dt.Rows[0][0].ToString();
con.Close();
String script = "window.onload = function() { UpdateTemp('" + dt.Rows[0][0].ToString()+ "'); };";
ClientScript.RegisterStartupScript(this.GetType(), "UpdateTemp", script, true);
}
}
}
The quality code should be revised but I managed to make it work.
I made 2 changes:
1 : I used the method "PageLoad" instead of "BindGrid" (do you really need this one ?)
2 : I filled the raw temperature value directly as a parameter (as it is a float on my side), you englobed your datarow value with simple quote, that's interpreted as string by javascript and the method "showValue" from "hand" seems to not tolerate it.
Code:
Result:
Some ideas to improve your code:
Put your connection string in configuration file (never in code !)
If you use the "using" do not matter calling the method "close" of your resource, it's automatically called by it. (behind the using it's in fact a try-catch-finally, the close is called anyway in the finally ;) )
If possible separate the data access from the rendering page, create a separate class that manages it, it will improve readability and evolution of your code.
If the variable "temp1" is not used, just remove it.
When you get a data (no matter the source, could be a webservice or database or else), check always if it's null before access it and log it, it doesn't cost a lot and you avoid escalation of exceptions.
We are in 2019, you could use Dapper to get directly object as result from your queries instead of a generic datarow. (check the website if interested they have a lot of interesting tutorials).
Kr,
Ali
Related
I am working on a tool for manual classification which changes the property of certain dot(color in my case) chosen in a scatter plot by bokeh. I changed the source data in callback by s.data = d2 and s.change.emit() but both failed. I thought such operation will change source.data, but when I print source.data, actually nothing happens.
The dots' color in the plot changes as expected though.
Here is my related code:
DF = pd.read_csv(csv_path)
s = ColumnDataSource(DF_file)
p = figure(plot_width=500, plot_height=500, tooltips=TOOLTIPS,tools="lasso_select, tap", title="manual classification")
circles = p.circle('x', 'y', color='color', size=10, source=s, line_alpha=0.6,fill_alpha=0.6)
s.callback = CustomJS(args=dict(s1=s), code="""
var inds = cb_obj.selected.indices;
var d1 = s1.data;
for (var i = 0; i < inds.length; i++)
{d1['color'][inds[i]] = 'green';}
s1.change.emit();
""")
Both print(s.data) and the csv file saved from s.to_csv(xxx) shows no change to the original input data.
Also, I wonder how does callback work to change the plot's data while leave the data in python unchanged when the data in python is the data passed to it in args=(s1=s).
I have searched for some possible methods and found this answer in https://discourse.bokeh.org/t/getting-selected-glyph-data-properties-via-customjs/4311/5?u=1113
when the Bokeh JS object is instantiated it uses the Python objects as sources for data but then is essentially disconnected from them - so updates to the JS model are not propagated back to their Python parents.
While the discussion in this webpage also proposed a workaround using IPython.notebook.kernel.execute to create or overwrite a variable in the Python. It can only be used in a notebook frontend(I found this workaround only works when use output_notebook() in the code).
Then here is my new code to change the original data in python:
s.callback = CustomJS(args=dict(s1=s1), code="""
var inds = s1.selected.indices;
var d1 = s1.data;
for (var i = 0; i < inds.length; i++)
{
d1['color'][inds[i]] = 'red';
var index = inds[i];
var command = "s1.data['color'][" + index + "] = red";
var kernel = IPython.notebook.kernel;
kernel.execute(command);
}
s1.change.emit();;
""")
JavaScript - Calculating values using numeric results from separate functions and display without user interaction.
I’m looking to calculate the percentage difference between returned API values (extracted from a single target API, but different ‘GET’ URL’s) and display the percentage differential.
I’m using separate functions to extract each value as the ‘GET’ URL is time stamped to perform searches for specific results.
Ive included only 2 functions, but would like to calculate the difference between more as required.
I’ve only included the Javascript, if there is a typo in the code, then it is only a typo, the functions display the relevant data, it is calculating the percentage difference between “BTC_Result_Now” (from the 1st function) and BTC_Result_1_Week (from the 2nd function) that proving not so straight forward:
var BTC_XHR_Now = new XMLHttpRequest();
function getBTC_Price_Now() {
var url = BTC_API_PriceNow;
BTC_XHR_Now.open('GET', url, true,);
BTC_XHR_Now.onload = function() {
var response = JSON.parse(BTC_XHR_Now.responseText);
var BTC_Result_Now = response.BTC.USD;
document.getElementById("BTC-Price-Now").innerHTML = BTC_Result_Now; // $17077.13
}
BTC_XHR_Now.send();
}
getBTC_Price_Now();
var BTC_XHR_1_Week = new XMLHttpRequest();
function getBTC_Price_1_Week() {
var url = BTC_API_Price1Week;
BTC_XHR_1_Week.open('GET', url, true,);
BTC_XHR_1_Week.onload = function() {
var response = JSON.parse(BTC_XHR_1_Week.responseText);
var BTC_Result_1_Week = response.BTC.USD;
document.getElementById("BTC-Price-1-Week").innerHTML = BTC_Result_1_Week; // $13749.57
}
BTC_XHR_1_Week.send();
}
getBTC_Price_1_Week();
I have a very simple application. My Home controller receives a Comment object and runs its logic to determine whether a notification needs to be displayed. If the answer is yes, then it sets the following parameters in ViewBag:
ViewBag.toDisplayNotification = 1;
ViewBag.notificationTitle = "This is the title";
ViewBag.notificationId = 2;
else, it sets the parameters as follow (I randomly set everything to null so that toDisplayNotification wouldnt be 1 anymore!)
ViewBag.toDisplayNotification = null;
ViewBag.notificationTitle = null;
ViewBag.notificationId = null;
It then displays the Comment partial View in which I have:
<script>
$(function myfunction() {
var toDisplayNotification = #Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(ViewBag.toDisplayNotification));
var notificationTitle = #Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(ViewBag.notificationTitle));
var notificationId = #Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(ViewBag.notificationId));
if(toDisplayNotification == 1){
var n = new Notification(notificationTitle, {
body: "This is where the body goes",
icon: '#Url.Action("GetImageByNotificationId", "Image", new { id = ViewBag.notificationId})'
});
}
});
</script>
So the issue I'm facing is that the view, regardless of the toDisplayNotification value, always displays the notification (I have tested the logic of my Home controller and know that it sets the correct values to each ViewBag property) even when the value of toDisplayNotification shouldnt be zero.
Is it possible that my ViewBag values are being changed somehow (can't be from the code since my Home controller displays the partial view directly so the values should remain unchanged in the transition) or am I missing something in my if condition?
Edit 1 - To answer some of the questions below. I am only using Newtonsoft.Json.JsonConvert.SerializeObject because someone in a different question had suggested that I use. Otherwise, I'm not a serialization expert (What I find is that unless, I serialize the property, I cannot pull non-integer values out of ViewBag into jquery/javascript).
Also, I did try replacing the toDisplayNotification line with either of the following but neither one worked:
var toDisplayNotification = #ViewBag.toDisplayNotification;
//or
var toDisplayNotification = #Html.Raw(ViewBag.toDisplayNotification);
Try this
var toDisplayNotification = #Html.Raw(ViewBag.toDisplayNotification);
if(toDisplayNotification == 1){
var n = new Notification(notificationTitle, {
body: "This is where the body goes",
icon: '#Url.Action("GetImageByNotificationId", "Image", new { id = ViewBag.notificationId})'
});
}
I'm not entirely sure why you are serializing ViewBag.toDisplayNotification and then comparing it to a number.
I have data with three dimensions x, y and z. I display them as a bokeh gridplot with three separate graphs below each other. The x-axes are linked so that if I pan in one of the graphs, the other ones are updated:
time = np.arange(len(data))
labels = ["x","y","z"]
plots = []
for i in range(3):
if i != 0:
fig = figure(plot_width=900, plot_height=150, x_range=first.x_range)
else:
fig = figure(plot_width=900, plot_height=150)
# First figure is saved as other figures are linked to this one
first = fig
source = ColumnDataSource({'x': time, 'y': data[:,i]})
fig.line('x','y', source=source)
fig.yaxis.axis_label = labels[i]
plots.append([fig])
p = gridplot(([plots[0], plots[1], plots[2]]))
show(p)
I also implemented a callback for the x-axes ranges which updates the graph each time the x-range changes so that the plot fills the complete graph on the y-axis:
fig.x_range.callback = CustomJS(args=dict(source=source, xrange=fig.x_range,
yrange=fig.y_range), code=adjust_y_range_js)
In this code, the range objects are set with the newly calculated ranges, which triggers an update in the graph from which the callback originated. I.e. if I zoom in on the first of the three graphs, the y-range of the first graph will be updated so that it fills the plot. Due to the link with the other plots, the x-ranges of the other two plots will change as well. However, their x_ranges won't trigger the callback. I tried passing the other ranges in the callback JS code and updating them in the following manner, but unsuccessfully:
xrange_other.trigger('change');
This will make the box selection tool freeze for some reason, i.e. it will permanently try to make a selection. Any ideas how to update the whole plot in another way?
The code for the callback is the following (taken from someone's github, but can't find the original source unfortunately):
adjust_y_range_js = """
function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
var data = source.get('data');
var start = yrange.get('start');
var end = yrange.get('end');
var time_start = xrange.get('start');
var time_end = xrange.get('end');
var pre_max_old = end;
var pre_min_old = start;
var time = data['x'];
var pre = data['y'];
t_idx_start = time.filter(function(st){return st>=time_start})[0];
t_idx_start = time.indexOf(t_idx_start);
t_idx_end = time.filter(function(st){return st>=time_end})[0];
t_idx_end = time.indexOf(t_idx_end);
var pre_interval = pre.slice(t_idx_start, t_idx_end);
pre_interval = pre_interval.filter(function(st){return !isNaN(st)});
var pre_max = Math.max.apply(null, pre_interval);
var pre_min = Math.min.apply(null, pre_interval);
var ten_percent = (pre_max-pre_min)*0.1;
pre_max = pre_max + ten_percent;
pre_min = pre_min - ten_percent;
if((!isNumeric(pre_max)) || (!isNumeric(pre_min))) {
pre_max = pre_max_old;
pre_min = pre_min_old;
}
yrange.set('start', pre_min);
yrange.set('end', pre_max);
// This is what I tried. When using these variables, I passed them in the CustomJS args dict, of course.
//yrange_other1.trigger('change');
//yrange_other2.trigger('change');
//xrange_other1.trigger('change');
//xrange_other2.trigger('change');
//soure_other1.trigger('change');
//soure_other2.trigger('change');
"""
I'm using python 2.7.10 and Bokeh version 0.12.3.
Using Joomla custom output module to display articles for a category, but have limited space.
Here is the test site: http://cshtest.camdendiocese.org/
In the footer you'll see the third of 4 columns has data. It scrolls forward and backwards using JavaScript. But as you see it has 4 columns, all of which work the same on different categories.
I'm having trouble with creating the other columns because they all will need to use separate data items, hence separate modules. I was trying to create an object but got stopped specifying a JavaScript object inside PHP.
Here is the JavaScript that is working:
// This script provides forward and backward paging for article intros in footer-1
// create an array of strings
// Each array element is initalized at this time to establish global scope
var mytexts = [];
mytexts[0] = "<h2>this is text string one</h2>";
mytexts[1] = "<h2>this is text string two</h2>";
mytexts[2] = "<h2>this is text string three</h2>";
// initialize variables
var txtNum = 0;
var txtLength = mytexts.length - 1;
// function to change string
function changeText(direction, col) {
// get next text number
txtNum = txtNum + direction;
// make sure we loop
if (txtNum > txtLength) {
txtNum = 0;
}
if (txtNum < 0) {
txtNum = txtLength;
}
// change the src attribute of the image
var colx = "col-" + col;
document.getElementById( colx ).innerHTML = mytexts[txtNum];
return false; // prevent default link
}
Here is the constructor I made to try to make an object:
function CatArticles(x0, x1, x2) {
// This script provides forward and backward paging for article intros in footer-1
// create an array of strings
// Each array element is initalized at this time to establish global scope
this.mytexts = [];
this.mytexts[0] = x0;
this.mytexts[1] = x1;
this.mytexts[2] = x2;
// initialize variables
this.Num = 0;
this.txtLength = this.mytexts.length - 1;
this.changeText = changeText;
}
// function to change string
function changeText (direction, col) {
// get next text number
this.Num = this.Num + direction;
// make sure we loop
if (this.Num > this.txtLength) {
this.Num = 0;
}
if (this.Num < 0) {
this.Num = this.txtLength;
}
// change the src attribute of the image
var colx = "col-" + col;
document.getElementById( colx ).innerHTML = this.mytexts[this.Num];
return false; // prevent default link
}
I can instantiate the object with script tags in PHP but can't think how to get the variable created in the html to be output. Do you have a suggestion or approach?
If I understand well, considering they are two different scripts, server side and client side another, the solution would be to use ajax for interaction, after the page has already been generated by php.