I'm working on a canvas JS application to facilitate graphing, it's a personal project.
An issue I'm having trouble with currently is scaling of variables to display visually.
for example, users enter a set of points, these can be any number. I cannot always 1:1 scale graph every-point. Imagine a canvas of 600x600 and values of (1000,1000) and (1,1). you would have to do some scaling/modification to decide where these points should be put on the graph.
How can one dynamically scale numbers like this and have them sit in reasonable places? Are there common approaches to solving this problem?
Yes, you can "map" source values into a designated range.
This mapRange function allows you to scale/map your 1000x1000 values into your 600x600 canvas
// Given low,high values of the source(1000,1000)
// Given low,hight values of the mapped numbers (600,600)
// and given a value to map from the source to the destination range (value)
// map the source value into a designated range
function mapRange(value, sourceLow, sourceHigh, mappedLow, mappedHigh){
return mappedLow + (mappedHigh - mappedLow) *
(value - sourceLow) / (sourceHigh - sourceLow);
}
Related
I am currently using chart.js to plot spectral data from a EMI receiver. There are over 16000 (x,y) data indexes within each dataset and therefore I have made it so only the lines show without any points.
I now have a list of a few certain (x,y) value pairs that I would like to put points/markers on and add tooltips for. Is there a way to add tooltips and/or points or markers to only certain (x,y) value pairs within each dataset?
Any help would be appreciated. I will update with any photos/code if needed as I currently do not have an attempt at a solution for this.
EDIT1:
As you can see, it is very difficult to select the maximum point of the peaks due to how many data points make up the chart. I want to select only the local maximums and display tooltips for those points.
options.elements.point has a prop called radius. Radius can take either a number or an array of numbers. A single number will determine the radius for all of the points in your chart but the array will be able to determine each element's radius. This way you are able to selectively determine each point's radius with full control.
A solution for your example might look like this:
const options = {
elements: {
radius: allPoints.map(point => {
const maxPoint = Math.max(allPoints)
// return radius 0 for every point that is not the max and radius 1 (or bigger if needed) for the maximum point
return point == maxPoint ? 1 : 0
})
}
}
Suppose I have a linechart with multiple lines (the number is dynamic) and I would need to always scale the Y so that all lines are shown - so the scale should always be based on the range with highest values. Is there a way how to it automatically? I found some example with automatic yScaling using d3.max but that is done for a known dataset. In my case, I do not know what range will be the one to use.
I'm using d3.js v4.
Is there a more convenient way to find the minimum and maximum values of a brush selection. This is meant to resize the y axis when I select a period in my brush area below.
Here is my method (everything is inside my function called when the brush is used) :
I find the extent limits of my selection
extent = d3.event.selection.map(chartComponent.x2().invert))
Then I have to redo an array containing all my selected points: I go on each point and compare it to the extent[0] or extent[1] to see if it is in the limits. I store the beginning and end point indices and then I use data.slice(begin, end) on my original data to get a new array.
Then apply d3.min and d3.max on the new array to find the min and the max level.
Then set the y axis to use theses limits.
chart.y().domain([chartComponent.ymin, chartComponent.ymax]);
chart.yaxisGraph.call(chartComponent.yAxis(true));
Do someone have a better idea ?
I have a Dygraph scatter chart of our data where I have instances of tightly grouped date-time stamps being graphed. The further away the mouse pointer is from the X axis the more difficult it seems to highlight those points. It's possible to highlight them, but it's very twitchy until zoomed in very tightly.
JSFiddle
For example, in order to view the point data associated with the points along the 4 or 6 or 8 RPM lines, you have to zoom in very-very tightly in both the X & Y axes. I want my users to be able to just point at a given point to get the associated data, without having to zoom in each time. Is there some way to reduce the proximity necessary, or precision of pointing to get the point-data from a given point?
Working Example:
Using the fiddle listed above, when zoomed all the way out, attempt to hover at the point that appears to be at: X: Aug 12, Y: 6 (it's actual data is: [new Date("2012/08/02 09:49:15"), 6.000000, 44190],. Notice that it's almost impossible to highlight that point as the points in close temporal proximity with lesser Y values are far more likely to be highlighted, even if you've got the cursor directly over the point.
It looked like .findClosestPoint might be what I was looking for. But reading the comments in the latest build for it I'm no longer so sure:
/**
* Given canvas X,Y coordinates, find the closest point.
*
* This finds the individual data point across all visible series
* that's closest to the supplied DOM coordinates using the standard
* Euclidean X,Y distance.
*
* #param {number} domX graph-relative DOM X coordinate
* #param {number} domY graph-relative DOM Y coordinate
* Returns: {row, seriesName, point}
*
#private
*/
This seems to imply that the Canvas coordinates are supplied programmatically, not via mouse pointer, but that's just a guess. Also, I haven't found any examples with it's use. Is there some way to make choosing a point less twitchy?
By default, dygraphs highlights corresponding points for every series (i.e. points which appear in the same row of the input data). This is typically what you want for time series plots, but as you've noted, for scatter plots where there may be multiple points in the same series with the same x-value, it breaks down.
The highlightSeriesOpts option, which is typically used to style the currently-highlighted series, has the side effect of changing the selection logic to be "closest by euclidean distance", rather than "closest by x-value". So you can achieve the effect you want by setting this option to an empty object:
new Dygraph(data, div {
highlightSeriesOpts: {}
})
Here's an updated version of your demo with the fix: JSFiddle
I have a dynamic data array that contains 3 ints that are used to build a pie chart. In most cases it works fine IE: [5, 10, 3]. The pie chart renders correctly and you see all the pieces.
However in some cases the numbers can be widely different. IE [1,500,250] or [400,1,2]. When this is the case you will only see the larger of the pie pieces and the smaller ones become so small they can not be seen; or clicked.
I need some way of correcting the data array for these cases. I have the ability to retain the true value while adjusting the display value so the pieces show up. What I am looking for is a check to see if it's necessary and then a relative number to adjust it by based on the other values.
Suggestions?
Well firstly I'd say you aren't so much "correcting" the data as fudging the data to meet your requirements.
Basically, there is a minimum percentage for which a slice of that proportion will be clickable and you will need to bring all pieces up to at least this size.
Of course - this can't work at the most extreme examples. If you had 1,000,000 slices all of the same value then no matter how you scaled them, some of them are going to be too small (or all of them).
You also need to be aware of how scaling certain very small slices will throw out the apparent proportions between other, larger, slices.
But - a very crude way of doing it could be something like...
var minPC = 0.5 , // the minimum %age slice visible
total; // total should be set to the sum of the values of all your slices
var minValue = total / 100 * minPC; // The smallest value visible (given the current total)
for (var slice in slices) { //assuming slices is a standard JS 'array'
if ( slices[slice] < minValue ) slices[slice] = minValue;
}
of course making the slices bigger like this will in turn increase the total - meaning that the small slices will still be less than the minimum visible percentage. You will need to make minPC sufficiently large to cope with this. And of course the more very small slices you have the worse this effect will be. You could account for this be re-scaling the larger slices.
However - I would advise you find a better way of the user interacting with the data by letting them select on/off slices - or by having slices 'explode'.
You seem to want to resize the segments of the pie if they are too small to make them visible/clickable.
May I suggest that instead of solving the problem this way (which would give an invalid
representation of the data), you could instead use labels outside of the pie chart to point at the segments? These labels could then, themselves, be made clickable.
The sum of the values in your array represent the entire "size" of the pie. The percentage of the pie each value has is the visual weight of that piece. You probably want to set a minimum threshold for the percentage size of each piece (the minimum threshold would be related to the diameter of your chart).
ie. [500, 490, 10] -> [500/1000, 490/1000, 10/1000] -> [50%, 49%, 1%]
If any value is less than your minimum threshold, you need to increase it to the minimum threshold and adjust your other values accordingly, so they all add up to 100%
It is related with fact that all points are sum and each value is calculated to pixels.