Projecting a Point onto a Line - javascript

I'm trying to find the red point coordinates in this image where the points are projected on to a line formed by the jagged line's start and end point.
I'm using the code from this article, What are the coordinates of the projected point on a line segment using the perp dot product ?, but it appears that I'm doing something incorrectly. Could someone please help me achieve the result in the above image. Thank you!
const points = [
{ x: 2, y: 2 },
{ x: 3, y: 1 },
{ x: 4, y: 4 },
{ x: 5, y: 3 },
{ x: 6, y: 6 },
{ x: 7, y: 5 },
{ x: 8, y: 6 }
];
const first = points[0];
const last = points.slice(-1)[0];
const d = {
x: last.x - first.x,
y: last.y - first.y
};
function dot_product(pt1,pt2)
{
return pt1.x*pt2.x + pt1.y*pt2.y;
}
function drawChart() {
let projected = [];
for (var i=0; i<points.length; i++)
{
p = points[i];
let e2 = {x:p.x-first.x,y:p.y-first.y};
let dot = dot_product(d,e2);
let len2 = d.x * d.x + d.y * d.y;
let v = {
x: first.x + (dot*d.x)/ len2,
y: first.y + (dot*d.y)/ len2,
}
projected.push(v);
}
chart = new CanvasJS.Chart("chartContainer", {
animationEnabled: false,
theme: "light2",
data: [
{
type: "line",
color: "blue",
lineDashType: "dash",
indexLabelFontSize: 16,
dataPoints: [points[0],points.slice(-1)[0]]
},
{
type: "line",
color: "blue",
indexLabelFontSize: 16,
dataPoints: points
},
{
type: "line",
color: "red",
indexLabelFontSize: 16,
dataPoints: projected
}
]
});
chart.render();
}
drawChart();
html
{
font-family: sans-serif;
}
body
{
width: 80%;
position: relative;
}
#chartContainer { height: 400px; width: 320px; }
<script src="https://canvasjs.com/assets/script/canvasjs.min.js"></script>
<div id="chartContainer"></div>

Related

Change size of bubble radius without changing r value

I'm adding data points to a bubble graph. However, since the value for r is very small I can barely see the points on the graph. I tried to use the radius property to expand the radius, but it seems to be overwritten by the r data property. How can I increase the radius for each bubble? For example, how can I set them to 10x the actual r value?
This is the code that I have so far:
this.managers.forEach(manager => {
const newDataPoint = {
label: [manager.SecurityName],
backgroundColor: this.getRandomRGB(),
borderColor: this.getRandomRGB(),
data: [{
x: +manager[this.selectedX],
y: +manager[this.selectedY],
r: +manager[this.selectedR]
}],
radius: (+manager[this.selectedR] * 10)
};
this.chartDataSet.push(newDataPoint);
});
I've managed to change the size by multiplying the r property directly, but that changes the actual value that shows up when hovered, which I want to avoid. How can I keep the r property the same but still increase the radius?
This can be done with the Plugin Core API. The API offers different hooks that may be used for executing custom code. In your case, you could use the afterDatasetUpdate hook to increase the radius of the points in the dataset's metadata.
plugins:[{
afterDatasetUpdate: chart => {
chart.getDatasetMeta(0).data.forEach(v => {
v._model.radius *= 10;
v._options.hoverRadius = v._model.radius;
})
}
}],
new Chart('canvas', {
type: 'bubble',
plugins: [{
afterDatasetUpdate: chart => {
chart.getDatasetMeta(0).data.forEach(v => {
v._model.radius *= 10;
v._options.hoverRadius = v._model.radius;
})
}
}],
data: {
datasets: [{
label: 'First Dataset',
data: [
{x: 10, y: 20, r: 1 },
{x: 20, y: 10, r: 2 }
],
backgroundColor: 'rgb(255, 99, 132)'
}]
},
options: {
scales: {
xAxes: [{
ticks: {
min: 5,
max: 25
}
}],
yAxes: [{
ticks: {
min: 5,
max: 25
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="canvas" height="90"></canvas>
For developers using version 3.9.1 it is like this:
const pixelValue = (scale, v) => {
const val = scale.getValueForPixel(v)
return Math.trunc(isNaN(val) ? v * 6 : 3)
}
const $chart = document.getElementById("chart");
const chart = new Chart($chart, {
type: "bubble",
data: {
datasets: [{
label: "scaled radius",
backgroundColor: 'rgb(255, 99, 132)',
data: [{
x: 10,
y: 11,
size: 1
},
{
x: 20,
y: 5,
size: 3
}
],
radius: context => {
const scale = context.chart.scales.y
const size = context.dataset.data[context.dataIndex].size
const value = Math.abs(pixelValue(scale, size))
return value
},
}]
},
options: {
maintainAspectRatio: false,
scales: {
x: {
min: 0,
max: 30
},
y: {
min: 2,
max: 14
}
}
}
})
.wrapper {
max-width: 800px;
max-height: 180px;
}
canvas {
user-select: none;
}
<div class="wrapper" style="height: 180px; width: 600px">
<canvas id="chart"></canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js" integrity="sha512-ElRFoEQdI5Ht6kZvyzXhYG9NqjtkmlkfYk0wr6wHxU9JEHakS7UJZNeml5ALk+8IKlU6jDgMabC3vkumRokgJA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

Is there a way to add Error bar to Echarts library

I am using Vue Js and Echarts library to build some graphs. I have a situation where I need to calculate the Standard Deviation and average for some data. The series are the average. I would like to add the error bar like the following screenshots to show the STD DEV in the graph.
Is there anyway to add the error bar to the Echart? I appreciate your effort and help !
Could this be what you want?
var categoryData = [];
var errorData = [];
var barData = [];
var dataCount = 50;
for (var i = 0; i < dataCount; i++) {
var val = Math.random() * 1000;
categoryData.push('category' + i);
errorData.push([
i,
echarts.number.round(Math.max(0, val - Math.random() * 100)),
echarts.number.round(val + Math.random() * 80)
]);
barData.push(echarts.number.round(val, 2));
}
function renderItem(params, api) {
var xValue = api.value(0);
var highPoint = api.coord([xValue, api.value(1)]);
var lowPoint = api.coord([xValue, api.value(2)]);
var halfWidth = api.size([1, 0])[0] * 0.1;
var style = api.style({
stroke: api.visual('color'),
fill: null
});
return {
type: 'group',
children: [{
type: 'line',
shape: {
x1: highPoint[0] - halfWidth, y1: highPoint[1],
x2: highPoint[0] + halfWidth, y2: highPoint[1]
},
style: style
}, {
type: 'line',
shape: {
x1: highPoint[0], y1: highPoint[1],
x2: lowPoint[0], y2: lowPoint[1]
},
style: style
}, {
type: 'line',
shape: {
x1: lowPoint[0] - halfWidth, y1: lowPoint[1],
x2: lowPoint[0] + halfWidth, y2: lowPoint[1]
},
style: style
}]
};
}
option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
title: {
text: 'Avg/Error chart'
},
legend: {
data: ['avg', 'error']
},
dataZoom: [{
type: 'slider',
start: 50,
end: 70
}, {
type: 'inside',
start: 50,
end: 70
}],
xAxis: {
data: categoryData
},
yAxis: {},
series: [{
type: 'scatter',
name: 'avg',
data: barData,
itemStyle: {
color: '#77bef7'
}
}, {
type: 'custom',
name: 'error',
itemStyle: {
normal: {
borderWidth: 1.5
}
},
renderItem: renderItem,
encode: {
x: 0,
y: [1, 2]
},
data: errorData,
}]
};
reworked from here
I'm not sure I understood correctly but you need to add new series with error data and change symbol to circle. Something like this: https://echarts.apache.org/examples/en/editor.html?c=boxplot-light-velocity
How did you try to do this? Please show you code.

How to add fill color in highcharts data series

I need to fill the color of data series dynamically based on the variable ( left , right , center ) from my code.
I have attached my code and expected output:
$(document).ready(function() {
var left = [
[4, 7],
[9, 2]
];
var right = [
[2, 2],
[9, 9]
];
var center = [
[4,5.5],
[10,5.5]
];
Highcharts.chart('container', {
chart: {
events: {
load: function () {
const xAxis = this.xAxis[0]
const yAxis = this.yAxis[0]
const leftBottom = {
x: xAxis.toPixels(right[0][0]),
y: yAxis.toPixels(right[0][1])
}
const leftTop = {
x: xAxis.toPixels(left[0][0]),
y: yAxis.toPixels(left[0][1])
}
const rightBottom = {
x: xAxis.toPixels(left[1][0]),
y: yAxis.toPixels(left[1][1])
}
const rightTop = {
x: xAxis.toPixels(right[1][0]),
y: yAxis.toPixels(right[1][1])
}
const leftMiddle = {
x: xAxis.toPixels(4),
y: yAxis.toPixels(5.5)
}
const rightMiddle = {
x: xAxis.toPixels(10),
y: yAxis.toPixels(5.5)
}
const leftTopMiddle = {
x: xAxis.toPixels(3.7),
y: yAxis.toPixels(6.5)
}
const leftBottomMiddle = {
x: xAxis.toPixels(2.1),
y: yAxis.toPixels(4)
}
const rightTopMiddle = {
x: xAxis.toPixels(9.8),
y: yAxis.toPixels(8)
}
const rightBottomMiddle = {
x: xAxis.toPixels(9.8),
y: yAxis.toPixels(3)
}
const curveTopLeft = this.curveTopLeft = this.renderer.path().attr({
d: `M ${leftMiddle.x} ${leftMiddle.y} Q ${leftTopMiddle.x} ${leftTopMiddle.y} ${leftTop.x} ${leftTop.y}`,
'stroke-width': 2,
stroke: 'red',
zIndex: 99
}).add()
const curveBottomLeft = this.curveBottomLeft = this.renderer.path().attr({
d: `M ${leftMiddle.x} ${leftMiddle.y} Q ${leftBottomMiddle.x} ${leftBottomMiddle.y} ${leftBottom.x} ${leftBottom.y}`,
'stroke-width': 2,
stroke: 'red',
zIndex: 99
}).add()
const curveTopRight = this.curveTopRight = this.renderer.path().attr({
d: `M ${rightMiddle.x} ${rightMiddle.y} Q ${rightTopMiddle.x} ${rightTopMiddle.y} ${rightTop.x} ${rightTop.y}`,
'stroke-width': 2,
stroke: 'red',
zIndex: 99
}).add()
const curveBottomRight = this.curveBottomRight = this.renderer.path().attr({
d: `M ${rightMiddle.x} ${rightMiddle.y} Q ${rightBottomMiddle.x} ${rightBottomMiddle.y} ${rightBottom.x} ${rightBottom.y}`,
'stroke-width': 2,
stroke: 'red',
zIndex: 99
}).add()
}
}
},
title: {
text: ''
},
tooltip: {
enabled: false
},
exporting: {
enabled: false
},
credits: {
enabled: false
},
plotOptions: {
series: {
pointStart: 1
}
},
xAxis: {
max: 10,
min: 1,
tickInterval: 1
},
yAxis: {
max: 11,
min: 0,
tickInterval: 1,
},
series: [{
showInLegend: false,
data: left
}, {
showInLegend: false,
data: right
},
{
showInLegend: false,
marker: {
enabled: true
},
data: center
}],
});
});
And my expected output should look like below,
You need to create a new path with no stroke but fill. The new path should be combined from the points you already defined.
const d = `M ${leftBottom.x} ${leftBottom.y}
Q ${leftBottomMiddle.x} ${leftBottomMiddle.y} ${leftMiddle.x} ${leftMiddle.y}
Q ${leftTopMiddle.x} ${leftTopMiddle.y} ${leftTop.x} ${leftTop.y}
L ${rightBottom.x} ${rightBottom.y}
Q ${rightBottomMiddle.x} ${rightBottomMiddle.y} ${rightMiddle.x} ${rightMiddle.y}
Q ${rightTopMiddle.x} ${rightTopMiddle.y} ${rightTop.x} ${rightTop.y}
Z`
const fillPath = this.renderer.path().attr({
d: d,
'stroke-width': 0,
fill: '#b19cd9',
zIndex: 1
}).add()
example: http://jsfiddle.net/r0j46wn6/24/
You can make use of the .css() method of your this.renderer.path() to set the fill style.
For example, Let's take the case of curveTopRight for what you can add that css as,
const curveTopRight = this.curveTopRight = this.renderer.path().css({
color: '#c8bfe7',
fill: '#c8bfe7'
}).attr({
d: `M ${rightMiddle.x} ${rightMiddle.y} Q ${rightTopMiddle.x} ${rightTopMiddle.y} ${rightTop.x} ${rightTop.y}`,
'stroke-width': 2,
stroke: 'red',
zIndex: 99
}).add()
However it is working only for the curved elements and not for the area's which are between the lines that you have drawn.
Here is a partially working example:
http://jsfiddle.net/r0j46wn6/23/
Hope this helps!

How to have chart.js automatically build x-axis labels based on x,y dataset?

Is there a way to have chart.js automatically display the range of labels that best fit and X,Y dataset?
I have a simple dataset that is zero based but with unequally spaced data, ala:
0.0, 7.0
0.5, 8.0
1.2, 9.0
...
49.9 213.0
Is there a way to pass this dataset into chart.js and have it do the right thing and compute, say, 10 labels from 0 -> 50.0 and display all of the datapoints connected by a line?
Edit:
Below Accepted version no longer works above version ^2.7.
For latest version 2.9 scatter series no longer display lines. So you need to use Line series with linear scale. So your options would like be below:
var randomColor = function(opacity) {
return 'rgba(' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + (opacity || '.3') + ')';
};
var scatterChartData = {
datasets: [{
label: "My First dataset",
fill: false,
lineTension: 0,
borderWidth: 3,
data: [{
x: 0.328,
y: 0.2
},{
x: 0.328,
y: 1.0
},
{
x: -0.328,
y: 1.0
},
{
x: -0.328,
y: 0.2
},
{
x: 0.0,
y: 0.0
},
]
}]
};
$.each(scatterChartData.datasets, function(i, dataset) {
dataset.borderColor = randomColor(0.4);
dataset.backgroundColor = randomColor(0.1);
dataset.pointBorderColor = randomColor(0.7);
dataset.pointBackgroundColor = randomColor(0.5);
dataset.pointBorderWidth = 1;
});
window.onload = function() {
var ctx = document.getElementById("canvas").getContext("2d");
window.myScatter = Chart.Line(ctx, {
data: scatterChartData,
options: {
title: {
display: true,
text: 'Chart.js Scatter Chart'
},
scales: {
xAxes: [{
type: 'linear',
position: 'top',
gridLines: {
zeroLineColor: "rgba(0,255,0,1)"
},
scaleLabel: {
display: true,
labelString: 'x axis'
}
}],
yAxes: [{
type: 'linear',
position: 'right',
gridLines: {
zeroLineColor: "rgba(0,255,0,1)"
},
scaleLabel: {
display: true,
labelString: 'y axis'
}
}]
}
}
});
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.bundle.js"></script>
<div style="width:75%">
<div>
<canvas id="canvas"></canvas>
</div>
</div>
From 2.1.3 to 2.7 you need to use showLines: true in your options object in the original answer.
Original answer:
This is possible out of the box with chart.js 2.x using the ability to pass x and y attributes with the data.
Here is an exmaple taken from the chart.js examples
var randomScalingFactor = function() {
return Math.round(Math.random() * 10);
};
var randomColor = function(opacity) {
return 'rgba(' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + (opacity || '.3') + ')';
};
var scatterChartData = {
datasets: [{
label: "My First dataset",
data: [{
x: 0.4,
y: randomScalingFactor(),
}, {
x: 1.6,
y: randomScalingFactor(),
}, {
x: 4.7,
y: randomScalingFactor(),
}, {
x: 9.2,
y: randomScalingFactor(),
}, {
x: 20.1,
y: randomScalingFactor(),
}, {
x: 44.5,
y: randomScalingFactor(),
}, {
x: 155.3,
y: randomScalingFactor(),
}]
}]
};
$.each(scatterChartData.datasets, function(i, dataset) {
dataset.borderColor = randomColor(0.4);
dataset.backgroundColor = randomColor(0.1);
dataset.pointBorderColor = randomColor(0.7);
dataset.pointBackgroundColor = randomColor(0.5);
dataset.pointBorderWidth = 1;
});
window.onload = function() {
var ctx = document.getElementById("canvas").getContext("2d");
window.myScatter = Chart.Scatter(ctx, {
data: scatterChartData,
options: {
title: {
display: true,
text: 'Chart.js Scatter Chart'
},
scales: {
xAxes: [{
position: 'bottom',
gridLines: {
zeroLineColor: "rgba(0,255,0,1)"
},
scaleLabel: {
display: true,
labelString: 'x axis'
}
}],
yAxes: [{
position: 'left',
gridLines: {
zeroLineColor: "rgba(0,255,0,1)"
},
scaleLabel: {
display: true,
labelString: 'y axis'
}
}]
}
}
});
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.3/Chart.bundle.js"></script>
<div style="width:75%">
<div>
<canvas id="canvas"></canvas>
</div>
</div>
For 3.8, the above answer again is outdated :/
You need this option for a line chart with linear X axis:
options: {
scales: {
x: {
type: 'linear'
}
}
}
If you add that to the example at https://www.chartjs.org/docs/3.8.0/charts/line.html (just below data: data,) then it will build a linear X axis. Because the default chart uses month names, to make it work properly you should open the "Setup" tab and change the labels to something like:
const labels = [1, 2, 3, 14, 15, 16, 17];
The X axis should now interpolate between the 3 and 14, leaving a large gap between data points:

Responsive CHART svg

I'm trying to do responsive chart... i used this but its not responsive just when i refresh the page it scaled to right width... Any other ideas then i had ?
And i try to set a div with viewBox and preserveAspectRatio.. But the same result..
var graph = new Rickshaw.Graph( {
element: document.querySelector("#chart"),
height: 100,
series: [{
color: '#1abc9c',
data: [
{ x: 0, y: 50 },
{ x: 1, y: 50 },
{ x: 2, y: 40 },
{ x: 3, y: 40 },
{ x: 4, y: 30 },
{ x: 5, y: 20 },
{ x: 6, y: 0 },
{ x: 7, y: 0 } ]
}]
});
graph.render();
var svg = d3.select('.chart-container').append("svg")
.attr("width", '100%')
.attr("height", '100%')
.attr('viewBox','0 0 '+Math.min(width,height)+' '+Math.min(width,height))
.attr('preserveAspectRatio','xMinYMin')
.append("g")
.attr("transform", "translate(" + Math.min(width,height) / 2 + "," + Math.min(width,height) / 2 + ")");
<div class="chart-container" id="chart"></div>
Ok thanks to Mark Sizer.. Solution is here :
var seriesData = [ [
{ x: 0, y: 50 },
{ x: 1, y: 50 },
{ x: 2, y: 50 },
{ x: 3, y: 40 },
{ x: 4, y: 40 },
{ x: 5, y: 20 },
{ x: 6, y: 0 },
{ x: 7, y: 0 } ] ];
var graph = new Rickshaw.Graph( {
element: document.getElementById("chart"),
renderer: 'area',
series: [
{
data: seriesData[0],
color: '#1abc9c'
}
]
} );
var resize = function() {
graph.configure({
width: window.innerWidth * 0,
height: window.innerHeight * 0.20
});
graph.render();
}
window.addEventListener('resize', resize);
resize();
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rickshaw/1.5.1/rickshaw.js"></script>
<div id="chart"></div>

Categories

Resources