I working on a project that needs me to use graph csv file inputs. I've been using plotly and so far it seems to be working very well.
However, when I try to change the color of the graphs (lines and markers) it doesn't work. I am posting excerpts of my code since the color is overall a small portion of the code and I don't want to dump everything here.
//There's multiple charts so changing color is important
var r = Math.random() * 256
var g = Math.random() * 256
var b = Math.random() * 256
...
//used these as a vars so I can change things to test easily (multiple time series being used)
var color='rgb('+r+', '+g+', '+b+')'
var colora='rgba('+r+', '+g+', '+b+', '+'0.14'+')'
...
//layout of markers
{
x: time,
y: time,
z: data1,
line: {
reversescale: false,
//color: "'"+color+"'"
color: "'rgb("+r+', ' +g+', '+ b+")'",
},
//mode: 'lines',
marker: {
//color: "'"+color+"'",
color: "'rgb("+r+', ' +g+', '+ b+")'",
size: 3,
line: {
//color: "'"+colora+"'",
color: "'rgb("+r+', ' +g+', '+ b+")'",
width: 0.1
},
opacity: 0.8
},
type: 'scatter3d'
}
Both the attempts just give me the standard black dots. When I tried constants that worked fine (something like color:'rgb(100,100,240)'). Is there something I'm missing here? I've console.logged this thing and it doesn't seem to be an issue with the structure of my vars.
You have too many quotation marks around your rgb strings. In order to avoid confusion when concatenating strings, you could also use template strings.
See the working fiddle below.
const r = 0;
const g = 255;
const b = 0;
const color = 'rgb(' + r + ',' + g + ',' + b + ')';
const colorTemplate = `rgb(${r},${g},${b})`;
var trace1 = {
x: [1, 2, 3, 4],
y: [10, 15, 13, 17],
type: 'scatter',
marker: {
color: color
}
};
var trace2 = {
x: [1, 2, 3, 4],
y: [16, 5, 11, 9],
type: 'scatter',
marker: {
color: colorTemplate
}
};
var data = [trace1, trace2];
Plotly.newPlot('myDiv', data);
<head>
<!-- Load plotly.js into the DOM -->
<script src='https://cdn.plot.ly/plotly-latest.min.js'></script>
</head>
<body>
<div id='myDiv'><!-- Plotly chart will be drawn inside this DIV --></div>
</body>
The problem with my code is that I'm using vars. By switching to consts, I can fix the thing. I didn't catch the color not changing because for some reason the legend was displaying the right colors, but the markers and lines don't accept it.
Related
I am interested in creating an interactive plot in plotly.js, where a user can click on the plot and add a new point at that position. Essentially this means retrieving the plot coordinates of a mouse click. Most of the built-in click events deal with capturing a click on an already plotted marker, rather than an arbitrary position within the plot.
The following question deals with this issue, Plotly.js create a point on click. However, the code appears to have been made obsolete by changes to plotly.js at some point. An example of the answer to that question was demo'd on codepen. It appears that the xy mouse position detection now only updates within the toolbar region. My guess is that there was a renaming in the various components that make up the plot.
Link to non-functioning code from the comments of that answers:
https://codepen.io/circleoncircles/pen/abObLLE
var traces = [{
x: [1, 2, 3, 4],
y: [10, 15, 13, 17],
mode: 'markers',
type: 'scatter'
}];
traces.push({
x: [2, 3, 4, 5],
y: [16, 5, 11, 9],
mode: 'markers',
type: 'scatter'
});
traces.push({
x: [1, 2, 3, 4],
y: [12, 9, 15, 12],
mode: 'markers',
type: 'scatter'
});
traces.push({
x: [],
y: [],
mode: 'markers',
type: 'scatter'
});
var myPlot = document.getElementById('myPlot')
Plotly.newPlot('myPlot', traces, {hovermode: 'closest'});
Number.prototype.between = function(min, max) {
return this >= min && this <= max;
};
Plotly.d3.select(".plotly").on('click', function(d, i) {
var e = Plotly.d3.event;
var bg = document.getElementsByClassName('bg')[0];
var x = ((e.layerX - bg.attributes['x'].value + 4) / (bg.attributes['width'].value)) * (myPlot.layout.xaxis.range[1] - myPlot.layout.xaxis.range[0]) + myPlot.layout.xaxis.range[0];
var y = ((e.layerY - bg.attributes['y'].value + 4) / (bg.attributes['height'].value)) * (myPlot.layout.yaxis.range[0] - myPlot.layout.yaxis.range[1]) + myPlot.layout.yaxis.range[1]
if (x.between(myPlot.layout.xaxis.range[0], myPlot.layout.xaxis.range[1]) &&
y.between(myPlot.layout.yaxis.range[0], myPlot.layout.yaxis.range[1])) {
Plotly.extendTraces(myPlot, {
x: [
[x]
],
y: [
[y]
]
}, [3]);
}
});
Plotly.d3.select(".plotly").on('mousemove', function(d, i) {
var e = Plotly.d3.event;
var bg = document.getElementsByClassName('bg')[0];
var x = ((e.layerX - bg.attributes['x'].value + 4) / (bg.attributes['width'].value)) * (myPlot.layout.xaxis.range[1] - myPlot.layout.xaxis.range[0]) + myPlot.layout.xaxis.range[0];
var y = ((e.layerY - bg.attributes['y'].value + 4) / (bg.attributes['height'].value)) * (myPlot.layout.yaxis.range[0] - myPlot.layout.yaxis.range[1]) + myPlot.layout.yaxis.range[1]
if (x.between(myPlot.layout.xaxis.range[0], myPlot.layout.xaxis.range[1]) &&
y.between(myPlot.layout.yaxis.range[0], myPlot.layout.yaxis.range[1])) {
console.log("Location X:"+x+" Y"+y)
document.getElementById("xvalue").value = x;
document.getElementById("yvalue").value = y;
}
});
Does anybody have an updated method for this?
I think I've got it. I would still consider this relatively hacky, so if someone else has a better way to handle it, I'm all ears and would be happy to assign a better answer.
All you really need to do is get the bounding box of the element that represents the plot field. It appears that one such element is that with the class gridlayer. Then I grabbed its bounding rect.
So the computation lines in the above would become:
var bgrect = document.getElementsByClassName('gridlayer')[0].getBoundingClientRect();
var x = ((e.x - bgrect['x']) / (bgrect['width'])) * (myPlot.layout.xaxis.range[1] - myPlot.layout.xaxis.range[0]) + myPlot.layout.xaxis.range[0];
var y = ((e.y - bgrect['y']) / (bgrect['height'])) * (myPlot.layout.yaxis.range[0] - myPlot.layout.yaxis.range[1]) + myPlot.layout.yaxis.range[1];
One additional change is necessary if you want to use the latest version of plotly. d3 is no longer embedded in plotly, so it's necessary to get d3 from CDN (the most recent version I found to work for this is v5).
<script src="https://cdn.plot.ly/plotly-2.11.0.min.js"></script>
<script src="//d3js.org/d3.v5.min.js"></script>
In the script you just reference d3 at the root then:
d3.select(".plotly").on('click', function(d, i) {
var e = d3.event;
I revised the codepen example that was posted previously to this new method https://codepen.io/jranalli/pen/eYyVVgr
Using marker:{color:x} in javascript plotly (http://jsfiddle.net/d8bt1qof/), I can color-code my
data:
But how can I change the colorscale?
Different colorscales seems to be available (https://plotly.com/javascript/colorscales/), but the usage is only explained for heatmap plots. And adding colorscale: 'Portland' seems not to work.
scattergl trace markers can also have a colorschale. I found a reference for it in the documentation here:
colorscale
Parent: data[type=scattergl].marker
Type: colorscale
Sets the colorscale. Has an effect only if in marker.coloris set to a numerical array. The colorscale must be an array containing arrays mapping a normalized value to an rgb, rgba, hex, hsl, hsv, or named color string. At minimum, a mapping for the lowest (0) and highest (1) values are required. For example, [[0, 'rgb(0,0,255)'], [1, 'rgb(255,0,0)']]. To control the bounds of the colorscale in color space, usemarker.cmin and marker.cmax. Alternatively, colorscale may be a palette name string of the following list: Greys,YlGnBu,Greens,YlOrRd,Bluered,RdBu,Reds,Blues,Picnic,Rainbow,Portland,Jet,Hot,Blackbody,Earth,Electric,Viridis,Cividis.
So an example based on your fiddle you could look like this:
var x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var trace1 = {
x: x,
y: x,
mode: 'markers',
marker: {
size: 20,
color: x,
colorscale: 'Greens'
},
};
Plotly.newPlot('myDiv', [trace1], {});
Here is an implementation for a custom colorscale based on the viridis colour scale R users will be familiar with.
var x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var y = vector_normalise(x);
var trace1 = {
x: x,
y: x,
mode: 'markers',
marker: {
colorscale: [
[0.000, "rgb(68, 1, 84)"],
[0.111, "rgb(72, 40, 120)"],
[0.222, "rgb(62, 74, 137)"],
[0.333, "rgb(49, 104, 142)"],
[0.444, "rgb(38, 130, 142)"],
[0.556, "rgb(31, 158, 137)"],
[0.667, "rgb(53, 183, 121)"],
[0.778, "rgb(109, 205, 89)"],
[0.889, "rgb(180, 222, 44)"],
[1.000, "rgb(253, 231, 37)"]
],
color: y,
size: 20,
},
};
Plotly.newPlot('myDiv', [trace1], {});
Below is my normalisation function, I have left it verbose to help with understanding. The input vec could be overwritten and returned to reduce local variables if desired.
function vector_normalise(vec) {
var vmin = Math.min(...vec);
var vmax = Math.max(...vec);
// calculate the delta to save time with big arrays
var vdelta = vmax - vmin;
// create an empty array to return
var vec_ret = [];
// push doesn't seem to like inline functions
var vnorm;
// iterate over the array/vector
vec.forEach(value => {
vnorm = (value - vmin) / vdelta;
vec_ret.push(vnorm);
})
return vec_ret
}
Edit: Turns out Viridis is one of the existing available palettes... 😉
I want to draw a multiline chart with a dataSet(multiple lists append in 1 list) where I know which one I will as X-axis and Y-axis.
let dataSet = [[1, 2, 3, 4], [10, 15, 13, 17], [16, 5, 11, 9]];
/**
X-axis = dataSet[0]
The remaining will be used as Y-axis*/
The example is taken from here. Where I have seen for plotting each line(here 2 times) variable is calling to set the data. In my case, Y-axis will appear near about 30 times and for each X-axis value will be the same. But I haven't found a dynamic solution where I can append the Y-axis value using a for loop or something like that. That means I want to call this data variable only 1 time and want to append all information of multi-chart there at instant.
I have added my approach here.
let dataSet = [[1, 2, 3, 4], [10, 15, 13, 17], [16, 5, 11, 9]];
function get_val (data){
let x = [];
for(let j = 1;j<data.length;j++)
{
x.push(data[j]);
}
//console.log("x: ",x);
return x;
}
var trace1 = {
x: dataSet[0],
y: get_val(dataSet), /* if write here get_val(dataSet)[0] then works fine*/
type: 'scatter'
};
/**
if you uncomment the following lines, result will be as like as the example of plotly JS
*/
/*
var trace2 = {
x: dataSet[0],
y: get_val(dataSet)[1],
type: 'scatter'
};
*/
var data = [trace1/*, trace2*/];
Plotly.newPlot('myDiv', data);
<head>
<!-- Load plotly.js into the DOM -->
<script src='https://cdn.plot.ly/plotly-latest.min.js'></script>
</head>
<body>
<div id='myDiv'><!-- Plotly chart will be drawn inside this DIV --></div>
</body>
So, I want to know is there any approach by following which I can add multiple time any axis within a single variable(here trace1).
I have understood(maybe) where was my lacking.
1/ At first I have realized that I have overlooked that the traces or datapoints of plotly is an array of objects.
2/ I have also tried to organize my data
I have given here the solution that I have done.
let dataSet = [[10, 20, 30, 40], [10, 15, -13, 17], [1-6, 5, 11, 20] ]
/** following function will take the data and return the dataTRace as per as plotly's requirements.*/
function make_trace({data, set_type = "scatter", set_mode = "lines"} = {}){
let dataPoint = [];
for(let i = 1; i<data.length; i++){
dataPoint.push({
x: data[0],
y: data[i],
mode: set_mode,
type: set_type,
name: 'y_' + i
});
}
return dataPoint;
}
/** following function will make the layout for the plot*/
function make_layout({given_title="the title is not provided",x_min=0,x_max=15,x_axis_title="x_axis", y_min=0,y_max=15, y_axis_title="y_axis"} = {})
{
let layout_object = {
title: {
text: given_title
},
showlegend: true,
xaxis: {
range: [x_min, x_max],
title: x_axis_title
},
yaxis: {
range: [y_min, y_max],
title: y_axis_title
},
};
return layout_object;
}
let fig_layout = make_layout({given_title:"x vs y1, y2 plot",
x_min: 10, x_max : 50, x_axis_title:"x",
y_min: -20, y_max: 20, y_axis_title : "y(1,2)"});
Plotly.newPlot('myDiv', make_trace({data : dataSet, set_type:"scatter", set_mode : "lines"}), fig_layout);
Plotly.newPlot('myDiv_1', make_trace({data : dataSet, set_type:"scatter", set_mode : "markers"}), fig_layout);
<head>
<!-- Load plotly.js into the DOM -->
<script src='https://cdn.plot.ly/plotly-latest.min.js'></script>
</head>
<body>
<div id='myDiv'><!-- Plotly chart will be drawn inside this DIV --></div>
<div id='myDiv_1'><!-- Plotly chart will be drawn inside this DIV --></div>
</body>
I'm trying to build some chart like this one:
Chart Visual
But the main struggle is to add two different series that complement each other.
I really appreciate any help that someone could give me.
Many thanks in advance.
You can achieve it, but the process of implementation is not so easy. I prepared the example which shows how to do that, and I will try to explain what I did, step by step.
First, you need to define your data array just like that:
var data = [40, 30, 10, 20]
Then define your chart configuration, and inside of chart.events.load function handler put whole logic of creating desired effect.
Next step is iterate on all data positions, and create its own specific point, series, yAxis and pane, basing on calculations like below:
load() {
var chart = this,
series = [],
panes = [],
yAxes = [],
radius = 112,
innerRadius = 88,
pointAngle,
prevPointAngle = 0,
pointPadding = (radius - innerRadius) / 4,
colors = Highcharts.getOptions().colors,
additionalPointPadding = 2;
data.forEach(function(p, i) {
pointAngle = (p * 360) / 100 // Calculate point angle
// Prepare pane for each point
panes.push({
startAngle: prevPointAngle + pointPadding + additionalPointPadding,
endAngle: (pointAngle + prevPointAngle) - pointPadding - additionalPointPadding,
background: [{
backgroundColor: '#fff',
borderWidth: 0
}]
})
// Prepare yAxis for specific pane
yAxes.push({
min: 0,
max: 100,
lineWidth: 0,
tickPositions: [],
pane: i
})
// Prepare series with specific point
series.push({
name: 'Exercise ' + i,
data: [{
color: colors[i],
radius: radius + '%',
innerRadius: innerRadius + '%',
y: 100,
percents: p
}],
yAxis: i
})
prevPointAngle += pointAngle
})
And finally, update our chart by new objects:
chart.update({
pane: panes,
yAxis: yAxes,
series: series
},true, true)
Last thing you have to know, that your chart configuration should have the same amount of empty objects in pane array, like the data positions, e.g:
var data = [10, 80, 10]
(...)
pane: [{},{},{}]
Here is the example which shows the final effect: https://jsfiddle.net/yamu5z9r/
Kind regards!
I'm working with Flot to create a bar chart. However, I need to add special styling to certain columns. Is this possible at all?
My HTML looks like this:
<div id="monthly-usage" style="width: 100%; height: 400px;"></div>
And my JS like this:
somePlot = null;
$(function() {
//Data from this year and last year
var thisYear = [
[3, 231.01],
[4, 219.65],
[5, 222.47],
[6, 223.09],
[7, 248.43],
[8, 246.22]
];
var lastYear = [
[3, 171.7],
[4, 130.62],
[5, 163.03],
[6, 166.46],
[7, 176.16],
[8, 169.04]
];
var usageData = [{
//Usage this year
label: "2014",
data: thisYear,
bars: {
show: true,
barWidth: .3,
fill: true,
lineWidth: 0,
order: 1,
fillColor: 'rgba(194, 46, 52, .85)'
},
color: '#c22e34'
}, {
//Usage last year to compare with current usage
label: "2013",
data: lastYear,
bars: {
show: true,
barWidth: .3,
fill: true,
lineWidth: 0,
order: 2,
fillColor: 'rgba(73, 80, 94, .85)'
},
color: '#49505e'
}];
//X-axis labels
var months = [
[0, "Jan"],
[1, "Feb"],
[2, "Mar"],
[3, "Apr"],
[4, "Maj"],
[5, "Jun"],
[6, "Jul"],
[7, "Aug"],
[8, "Sep"],
[9, "Okt"],
[10, "Nov"],
[11, "Dec"]
];
//Draw the graph
somePlot = $.plot(('#monthly-usage'), usageData, {
grid: {
color: '#646464',
borderColor: 'transparent',
hoverable: true
},
xaxis: {
ticks: months,
color: '#d4d4d4'
},
yaxis: {
tickSize: 50,
tickFormatter: function(y, axis) {
return y + " kWh";
}
},
legend: {
show: false
}
});
var ctx = somePlot.getCanvas().getContext("2d"); // get the context from plot
var data = somePlot.getData()[0].data; // get your series data
var xaxis = somePlot.getXAxes()[0]; // xAxis
var yaxis = somePlot.getYAxes()[0]; // yAxis
var offset = somePlot.getPlotOffset(); // plots offset
var imageObj = new Image(); // create image
imageObj.onload = function() { // when finish loading image add to canvas
xPos = xaxis.p2c(data[4][0]) + offset.left;
yPos = yaxis.p2c(data[4][1]) + offset.top;
ctx.drawImage(this, xPos, yPos);
xPos = xaxis.p2c(data[5][0]) + offset.left;
yPos = yaxis.p2c(data[5][1]) + offset.top;
ctx.drawImage(this, xPos, yPos);
};
imageObj.src = 'path/to/file.png'; // set it's source to kick off load
});
});
Optimally, I would like to insert an icon in bar 5 and 6 that warns the user. Alternatively, I'd like to change the color of bars 5 and 6. Any ideas on how to fix this?
EDIT: I've updated my JS according to Mark's answer which works.
#Mark, how can I position the images correctly. They are a bit off. I need the image inside the red bar and not besides the bar. I'm trying to finetune this but it doesn't seem as if I can use for instance "0.5". I use side by side bars which is different from your version.
xPos = xaxis.p2c(data[4][0]) + offset.left;
yPos = yaxis.p2c(data[4][1]) + offset.top;
You can't do exactly what you ask with standard options, but there are a couple of possible approaches:
Write your own draw method and use the hooks to install it in place of the standard flot drawing code. This obviously entails a lot of work, but you'll have complete control over how to render your data. (That said, I wouldn't recommend it.)
Break your data into two different data sets. One data set would have dummy values (e.g. 0, or whatever your minimum is) for bars 5 and 6. The second data set would have dummy values for all bars except 5 and 6. You could then style the "two" data sets independently, giving each, for example a different color. Graph the two sets as a stacked bar chart with whatever additional styling tweaks are appropriate for your chart.
(As a FYI, there's a fair bit of information and examples at jsDataV.is. Look at the "Book" section; chapter 2 is dedicated to flot.)
flot gives you access to the HTML5 Canvas it's drawing on; so you just add your icon on there yourself. Borrowing from my own answer here.
var ctx = somePlot.getCanvas().getContext("2d"); // get the context from plot
var data = somePlot.getData()[0].data; // get your series data
var xaxis = somePlot.getXAxes()[0]; // xAxis
var yaxis = somePlot.getYAxes()[0]; // yAxis
var offset = somePlot.getPlotOffset(); // plots offset
$.get("someImage.txt", function(img) { // grad some image, I'm loading it from a base64 resource
var imageObj = new Image(); // create image
imageObj.onload = function() { // when finish loading image add to canvas
var xPos = xaxis.p2c(data[4][0]) + offset.left;
var yPos = yaxis.p2c(data[4][2]) + offset.top;
ctx.drawImage(this, xPos, yPos);
xPos = xaxis.p2c(data[5][0]) + offset.left;
yPos = yaxis.p2c(data[5][3]) + offset.top;
ctx.drawImage(this, xPos, yPos);
};
imageObj.src = img; // set it's source to kick off load
});
Example here.
Looks like: