Highstock Series Compare To Previous Day - javascript

I've implemented the HighStock chart shown at:
http://www.highcharts.com/stock/demo/compare
This chart shows percent change from the first, or left-most, data point. I've got some stock analysts telling me this isn't of much use, and that they would rather see percent change from the previous data point, or in other words, if I was looking at "today" on the chart, I would want to see percent change from yesterday. I've looked over the highstock API and I can't seem to find a way to define the percent change function. Is there a proper way to have highstock do this or should I use a highcharts line chart to accomplish this?

I had to do something similar in highcharts before. I wanted to get the actual change from one datapoint to the next.
What I do, is I take a look at the current point (this.x) and I find it's position in the series array. From there, I easily find the index of the previous point (index = a - 1) and do the math. Hope this helps :)
EDIT:
Looked into the stockcharts API and figured it out for that product:
tooltip: {
formatter: function() {
var s = '<b>'+ Highcharts.dateFormat('%A, %b %e, %Y', this.x) +'</b><br>';
$.each(this.points, function(i, point) {
s += '<span style="font-weight: bold; font-size: 1.2em; color: ' + point.point.series.color + ';">' + point.point.series.name + '</span>:<span style="font-weight: bold; font-size: 1.2em;">' + point.y + '</span>' ;
s += '<span style="font-size: .8em;">';
var target = this.x;
index = 0;
for (var a = 0; a < point.series.points.length; a++) {
if (point.series.points[a].x == target)
{
index = a - 1;
}
}
var delta = 0;
var change = 0;
if (index >= 0)
{
delta = this.y - point.series.points[index].y;
change = (delta / point.series.points[index].y) * 100;
}
s += Math.round(change,2) + '%</span>';
s +='</span><br>';
});
return s;
}
},
And a working jsfiddle: http://jsfiddle.net/nLjsc/2/

Related

Cartesian3.distance - returning NaN?

I'm trying to find the distance between a mouseclick and the nearest of a set of billboards. My code is as follows:
var smallestDistance = -1;
var currentDistance = -1;
var nearestPeak;
var peakPosition;
var mousePosition = new Cesium.Cartesian3(ev.clientX, ev.clientY, 0.0);
for (var i = 0; i < peaks.length; i++) {
peakPosition = peaks[i].position;
currentDistance = Cesium.Cartesian3.distance(mousePosition, peakPosition);
console.log( 'CESIUM: Distance to ' + peaks[i].name + ': ' + currentDistance + ' units.' );
if (currentDistance < smallestDistance) {
smallestDistance = currentDistance;
nearestPeak = displayedPeaks[i];
}
}
console.log( 'CESIUM: nearest peak is peak ' + nearestPeak.peakId );
if (smallestDistance != -1 && smallestDistance < 100000) {
selectedPeak = nearestPeak;
}
The issue appears in the first console.log statement, where it displays:
CESIUM: Distance to peak 1: NaN units.
According to Cesium's documentation, it should be returning a number. I could swear I did something similar in a past project and didn't have this issue. I have a strong suspicion that I am missing and/or forgetting something very basic. Can anyone advise?
EDIT:
I did some further debugging, and I am finding that peakPosition.x and peakPosition.y are both undefined, where peakPosition = peaks[i].position. peaks[i] is still rendering as a billboard on the globe, and peakPosition is an object. According to the Cesium documentation, it SHOULD be an object of type Cartesian3, which should have x and y members. So why would those members be undefined?
EDIT 2:
I tried replacing this line:
peakPosition = peaks[i].position;
with this:
peakPosition = new Cesium.Cartesian3(this.peaks[i].position.x, this.peaks[i].position.y, 0.0);
and its x, y, and z members are always 0, 0, and 0. I can't figure this out for the life of me.
So, per our discussion we have discovered:
peaks is an array of Cesium.Entitys.
and
the position field of a Cesium.Entity is a Cesium.PositionProperty, which is an interface that has a method getValue, accepting a Cesium.JulianDate as its first parameter. So, putting all that together, the only change you need is:
peakPosition = peaks[i].position;
to
peakPosition = peaks[i].position.getValue(new Cesium.JulianDate());

How to give fixed width to legend in plotly.js

I am using plotly.js to create charts. Some charts have long text inside legend, so these text make legend unnecessary bigger. I want to give legend fixed width and if a length of text is long, I want it to be wrapped.
I have been trying to do this by manipulating svg element on DOM, but since legend is svg:g tag, I can not really do much. I also try give textLength to <text>, but it does wrap text but it does not make a new line.
Is there a way to give legend fixed width?
Perhaps just adding </br> to the legend text would be an easy solution to get a fixed width?
You could use any text wrapping trick in Javascript to do so, e.g.
for (i = 0; i < data.length; i += 1) {
data[i].name = data[i].name.replace(/([\w\s,]{13,}?)\s?\b/g, "$1</br>");
}
var numbers = [];
var total = 10000;
var i = 0;
var data = [];
for (i = 0; i < total; i++) {
numbers[i] = ((Math.random() + Math.random() + Math.random() + Math.random() + Math.random() + Math.random()) - 3) / 3;
}
data.push({
x: numbers,
name: "that's a histogram with lots of points, it therefore needs lots of text as well",
type: 'histogram',
marker: {color: 'blue'}
});
numbers = [];
for (var i = 0; i < total; i++) {
numbers[i] = 0.25 + ((Math.random() + Math.random() + Math.random() + Math.random() + Math.random() + Math.random()) - 3) / 3;
}
data.push({
x: numbers,
name: "that's yet another histogram with lots of points, it therefore needs lots of text as well, like for example that it was shifted by 0.25",
type: 'histogram',
marker: {color: 'red'}
});
for (i = 0; i < data.length; i += 1) {
data[i].name = data[i].name.replace(/([\w\s,]{13,}?)\s?\b/g, "$1</br>");
}
Plotly.newPlot('myDiv', data, {showlegend: true});
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="myDiv"></div>
Maximilian's answer almost works with the current version of Plotly.js. There doesn't seem to be a property for setting the width of the legend, but you can rig it by inserting breaks (</br>) into the legend text manually. For some reason Plotly seems to mishandle the first break in the text, however; to get around this, you can add a "ghost" break at the beginning of the text. For instance:
// Rudimentary function to break a string on spaces
// attempting to get no more than `maxChars` characters in a line
const breakString = (str, maxChars) => {
if (str.length > maxChars && str.includes(' ')) {
let index = str.lastIndexOf(' ', maxChars);
if (index === -1) index = str.indexOf(' ', maxChars + 1);
return str.substr(0, index) + '</br>' + breakString(str.substr(index + 1), maxChars);
} else {
return str;
}
}
const original_text = "Respondents who think Tenet is Christopher Nolan's most interesting movie";
const final_text = '</br>' + breakString(original_text, 20);
The color symbols next to each legend entry by default are aligned to the center of the corresponding text; with multiline legend text, you'll probably want to change the symbols to be aligned to the top of the text. To do that, add this to your layout:
legend: {
valign: 'top',
}
This works for me using Plotly with javascript.
title: 'first part' + '<br>' + 'Second part'
eg
yaxis2: {
domain: [0.7, 1],
title: 'Sea Surface' + '<br>' + 'Temperature',
showgrid: true,
showline: true,
titlefont: {color: 'blue'},
tickfont: {color: 'blue'},
side: 'right',
anchor: 'free',
position: 0.91
},

Nested for loop updating 3 variables at the same time

I have looked around, but found no solution.
I want to print out the 'interpolation' points between this:
.card.ani25 {
bottom: 72%;
left:85%;
}
and this:
.card.ani49 {
bottom: 46%;
left: 46%;
}
Basically, I am trying to update all numerical values in that CSS at the same time. Updating one value at a time works, but is of no use in this case.
What I have tried so far, well, everything coming to mind, including triggering some infinite loops, but this is what is "stable":-)
for (var i = 25; i < 50; i++){
for (var j = 72; j >=46; j-=2){
for (var k = 85; k >=46; k-=3){
t.innerHTML = '.card.ani' + i + ' {' + '<br>' + 'bottom: ' + j + '%;' + '<br>' + 'left: ' + k + '%;' + '<br>' + '}';
}
}
}
This only prints out the final iteration, as can be seen here:
http://codepen.io/damianocel/pen/ZLEXOR
If I put a += after the "innerHTML2, it blows up, screen freezes, just like an infinite loop.
I know, the outermost loop runs first, then the next inner loop second multiple times, but I have never seen a case with 3 variables. Honestly, I wouldn't know the solution for 2 variables only either.
Thanks and very sorry if this has been asked before.
The problem is is that the operation for changing HTML through reassigning innerHTML is slow. When you use += it will rewrite the HTML each time, that's what's causing it to slow down. That said it still works, a faster way to do this is use a variable to hold the string and update that variable. Then at the end assign the value of t.innerHTML to that string:
var t = document.getElementById('target');
var str = "";
for (var i = 25; i < 50; i++){
for (var j = 72; j >=46; j-=2){
for (var k = 85; k >=46; k-=3){
str += '.card.ani' + i + ' {' + '<br>' + 'bottom: ' + j + '%;' + '<br>' + 'left: ' + k + '%;' + '<br>' + '}';
}
}
}
t.innerHTML = str;
Edit
After clarification it appears you only want a single loop and update the variables each time in that loop. In that case you can do something like:
var t = document.getElementById('target');
var str = "";
for (var i = 25, j = 72, k = 85; i < 50 && j >=46 && k >=46; i++, j-=2, k-=3){
str += '.card.ani' + i + ' {' + '<br>' + 'bottom: ' + j + '%;' + '<br>' + 'left: ' + k + '%;' + '<br>' + '}';
}
t.innerHTML = str;
For each section inside a for loop for(x;y;z) you can use a comma to make many statements. So in the first section you can define 3 variables and in the last section update 3 variables. The middle section runs the loop as long as all of those conditions are met, when a single one fails it stops.
Note: You can use || (OR) if you would like it to keep going as long as one of the conditions are true.
Code Pen (single loop)
Code Pen
I'd promote a very different approach.
I think of this as a number of distinct ranges that need to be animated over in parallel. So I would like to have a function that knows how to do just that.
const rangeFns = (...configs) => fn => val =>
fn.apply(null, configs.map(cfg => (cfg.end - cfg.start) * val + cfg.start))
This takes a collection of range descriptions and returns a function that accepts a custom function with one parameter for each of those ranges and finally returns a simple function that accepts a number between 0 and 1, returning the result of your custom function with the interpolation for each range being supplied for the respective parameter. That's a bit of a mouthful, but I think it's easy to use:
const anim = rangeFns(
{start: 25, end: 49},
{start: 72, end: 46},
{start: 85, end: 46}
)((ani, bottom, left) => `
.card.ani${Math.round(ani)} {
bottom: ${bottom}%;
left: ${left};
}`
);
If you ran this with values 0, 0.25, 0.5, 0.75, and 1, you'd get these results:
.card.ani25 {
bottom: 72%;
left: 85;
}
.card.ani31 {
bottom: 65.5%;
left: 75.25;
}
.card.ani37 {
bottom: 59%;
left: 65.5;
}
.card.ani43 {
bottom: 52.5%;
left: 55.75;
}
.card.ani49 {
bottom: 46%;
left: 46;
}
(Obviously you could add rounding to bottom and left if that's desired.)
You could then use this in an animation, by passing values calculated from your start and finish times.
To my mind the advantage is that this is more explicit. It makes the ranges more declarative, and makes the function that uses the results a lot more straightforward.
A version of this is available on Codepen.
Update
A slightly better version of this would be to use arrays rather than objects. (I originally had additional parameters in there that turned out unnecessary. I didn't notice that removing them allowed for a nice clean-up.)
const multiRange = (...ranges) => fn => val =>
fn.apply(null, ranges.map(range => (range[1] - range[0]) * val + range[0]))
const anim = multiRange([25, 49], [72, 46], [85, 46])((ani, bottom, left) => `
.card.ani${Math.round(ani)} {
bottom: ${bottom}%;
left: ${left};
}`
);
I think this reads better.
This version is also on Codepen.

Cycling through RGB colors in hex

I am creating a color picker type tool using a table. I want to apply a color to each cell in 16 by 16 table. I am having trouble with cycling through colors probably because I'm not sure how to go about it. How should I do this?
Hexagonal
You can use this BaseConverter. Make a loops that writes decimal numbers in 0-255 range with given interval, converts them to hexagonal and create a string containing hex number of three colors with a "#" before them (rgb).
It will work the same way as snippet below but you need to convert numbers additionaly: "rgb(" + (i * interval) + ", " + (j * interval) + ", " + (k * interval) + ")" will be changed to "#" + bin2hex(i * interval) + bin2hex(j * interval) + bin2hex(k * interval).
Decimal with an example
Otherwise just use the other format of CSS Colors which is rgb(red, green, blue) which takes decimal numbers as arguments.
/* Please keep in mind that this function is just an example.
You can modify it to create different palette schemes. */
var tot = 800; /* total amount of blocks */
var num = Math.pow(tot, 1/3); /* amount of blocks in one axis */
var interval = parseInt(255 / num); /* color diff step */
var cont = document.getElementById("container");
var tmp = 0;
for (i = 0; i < num; i++) {
for (j = 0; j < num; j++) {
for (k = 0; k < num; k++) {
/* creates block, add styled class and set color */
var block = document.createElement("div");
block.className = "foo";
block.style.backgroundColor = "rgb(" + (i * interval) + ", " + (j * interval) + ", " + (k * interval) + ")";
cont.appendChild(block);
/* i - red, j - green, k - blue */
tmp++;
}
}
}
.foo {
width: 20px;
height: 20px;
float: left;
}
<!-- It will be populated with JS -->
<div id="container"></div>
If I get the idea of your question wrong then sorry. That's how I would understand "cycle".
The problem you might encounter is that rgb() takes three arguments and you want to create two dimensional array with it. Try looking for rgb palette in google graphics to get the idea how RGB palette may look like and then change the code from above.

jquery flot chart highlight dot when near point

I am making a chart using jQuery flot (plot)
https://jsfiddle.net/5gtqwkjg/2/
var updateLegendTimeout = null;
var latestPosition = null;
function updateLegend() {
updateLegendTimeout = null;
var pos = latestPosition;
var axes = plot.getAxes();
if (pos.x < axes.xaxis.min || pos.x > axes.xaxis.max || pos.y < axes.yaxis.min || pos.y > axes.yaxis.max) {
return;
}
/*
var o = plot.pointOffset({ x: pos.x, y: -1.25 });
var ctx = plot.getCanvas().getContext("2d");
ctx.beginPath();
ctx.moveTo(o.left, o.top);
o.top = 0;
ctx.lineTo(o.left, o.top);
ctx.stroke();
*/
var i, j, dataset = plot.getData();
var halfDist = (dataset[0].data[1][0] - dataset[0].data[0][0]) / 2;
for (i = 0; i < dataset.length; ++i) {
var series = dataset[i];
// Find the nearest points, x-wise
for (j = 0; j < series.data.length; ++j) {
if (series.data[j][0] - halfDist > pos.x) {
break;
}
}
// Now Interpolate
var y,
p1 = series.data[j - 1],
p2 = series.data[j];
if (p1 == null) y = p2[1];
else if (p2 == null) y = p1[1];
else y = p1[1];
legends.eq(i).text(series.label.replace(/=.*/, "= " + y.toFixed(2)));
//dataset[i].highlightColor = "#abcdef";
//plot.highlight(dataset[0].series, dataset[0].datapoint);
}
}
$("#placeholder").bind("plothover", function (event, pos, item) {
latestPosition = pos;
if (!updateLegendTimeout) {
updateLegendTimeout = setTimeout(updateLegend, 50);
}
});
I want to add in a functionality that when the user moves the mouse along the x-axis the dot will highlight to indicate what point they are hovering nearest to. I already have the legend reflect the values but how would I highlight the dots?
EDIT: Very helpful answers guys! Here is the finished result if anyone is interested
https://jsfiddle.net/5gtqwkjg/4/
You can make use of the highlight and unhighlight functions provided by Flot.
highlight(series, datapoint)
Highlight a specific datapoint in the data series. You can either
specify the actual objects, e.g. if you got them from a "plotclick"
event, or you can specify the indices, e.g. highlight(1, 3) to
highlight the fourth point in the second series (remember, zero-based
indexing).
unhighlight(series, datapoint) or unhighlight()
Remove the highlighting of the point, same parameters as highlight.
If you call unhighlight with no parameters, e.g. as
plot.unhighlight(), all current highlights are removed.
See https://github.com/flot/flot/blob/master/API.md#plot-methods for reference.
Applying that logic to your question, I think I managed to create the desired result you were looking for.
I first start by unhighlighting everything, just to make sure nothing slips past us when we do highlight points.
for (i = 0; i < dataset.length; ++i) {
plot.unhighlight(); // Unhighlight everything!
var series = dataset[i];
Next up we go do the fun part, highlight all the points! (Just the ones we actually want to highlight)
In your "Find the nearest points, x-wise" loop I added another loop!
for (j = 0; j < series.data.length; ++j) {
if (series.data[j][0] - halfDist > pos.x) {
for(a = 0; a < dataset.length; a++) { // <-- The added loop
// You might want to optimize the way this is done
// The way you were storing the series data didn't seem to work like I..
// ..wanted it do, so I had to iterate the dataset variable again.
// The yellow line won't highlight if you change dataset[a] to series.
plot.highlight(dataset[a], series.data[j][0]);
}
break;
}
}
The result https://jsfiddle.net/qj3068zn/6/, for ease of use.
Do note, none of this is optimized. You're probably better off restructuring your code to provide a more general way to approach this and increase reusability and readability.
Using the highlight() function like Michel de Nijs in his answer, but a simpler version:
1) Put the plot.unhighlight(); at the start of your updateLegend function (you might also want to rename that since it not longer only updates the legend).
2) Add plot.highlight(i, j-1); after your for (j ...) loop.
See this fiddle for the code.

Categories