Open Amnesthesia opened 6 years ago
Good suggestion. The real question is, when would you need this? The only real use case I can think of is when you need, as you called them, "in-between" values, e.g for x = new Date(2018, 06, 26, 12)
. Then my follow up question is, why should you display values that aren't really in the data set?
You'd need this when you show linear data, and want to be able to e.g show decorators on touch. If you want to have a "draggable" decorator, e.g a vertical line that shows where on the graph you are, and what values the graph is showing there, as you drag across the graph, would be much smoother and less choppy, if you could show what the graph is actually showing, instead of showing the nearest data point.
In the case of the above examples, if you were to drag over that, you'd be showing 10 while the graph is going upwards to 14 and only after 15 would you suddenly be showing 20. You then have the option to not allow a smooth drag (and instead snap to data points, which becomes choppy), or just accept that the values will shift in staccato as you drag across
This makes most sense for AreaCharts and LineCharts I suppose, rather than bar charts etc. Since the graphs plot increase and decline, it would definitely be useful to see what the decrease/incline values look like, especially since that's what the graph is already doing (just visually instead of numerically).
Maybe getting the Y value for a given X coordinate would make more sense?
I hear you. I'll have to look into if d3.js have that option anywhere, since that's what I'm using to generate the paths.
<XAxis xAccessor={({ item }) =>
${item}} .... />
Did you guys dig into this? :)
Solved: The key here is to use bisect on the svg path.
@edo1493, I know this is over a year old, but I can't seem to find the proper solution. Is there an example that you would be able to point to?
Solved: The key here is to use bisect on the svg path.
Any chance you could please elaborate on your solution, @edo1493? A simple code or pseudo-code example would help a lot. Thanks
For anyone having this issue, here's my solution:
Step I: Get the SVG String
First, I bring the SVG string generated internally by area-chart.js
into my React component's state. I do this by mounting this extractor component as a child of the chart:
const LineExtractor = ({ line }) => {
this.setState({ line });
return null;
};
The line
prop comes from chart.js
, which passes extraProps
to all chart child components. You could also use this prop directly if you're building a child component.
Step II: Iterate the SVG to find the Y Value
Next, I get the Y value from the extracted SVG. To do that, I found a browser script which uses binary search (instead of the bisect function): https://www.freecodecamp.org/news/how-to-build-react-native-charts-with-dynamic-tooltips-64aefc550c95/
Unfortunately, this script requires the getPointAtLength
, and getTotalLength
browser API functions. After some research, I found a pure JS alternative called svg-path-properties
: https://www.npmjs.com/package/svg-path-properties
The final script to calculate the Y-Value in React Native then looks like this:
// REFERENCE: This code is an adaptation of the browser-based solution found here (https://www.freecodecamp.org/news/how-to-build-react-native-charts-with-dynamic-tooltips-64aefc550c95/)
import * as path from 'svg-path-properties';
function findY(p, x) {
const properties = path.svgPathProperties(p);
var pathLength = properties.getTotalLength()
var start = 0
var end = pathLength
var target = (start + end) / 2
// Ensure that x is within the range of the path
x = Math.max(x, properties.getPointAtLength(0).x)
x = Math.min(x, properties.getPointAtLength(pathLength).x)
// Walk along the path using binary search
// to locate the point with the supplied x value
while (target >= start && target <= pathLength) {
var pos = properties.getPointAtLength(target)
// use a threshold instead of strict equality
// to handle javascript floating point precision
if (Math.abs(pos.x - x) < 0.001) {
return pos.y
} else if (pos.x > x) {
end = target
} else {
start = target
}
target = (start + end) / 2
}
}
Within my React component, I get the Y-Value with:
findY(this.state.line, x);
It should also possible to do this with bisect by first iterating through the SVG and creating an array of values in the x domain.
It's a little messy but it works for now =)
What is the problem?
Hope I'm not being too active on these issues right now 🤞 This is more like an idea / feature: Getting the Y value for a given X value. When the XAxis is linear (e.g time), it would be nice to have a function like
x()
andy()
but not for the coordinates, but for the values. For example, if you have the following input:The graph is now going to be painted as if the values were like so:
So, by providing the
x
value, in this case time, it should be possible to find out not only what coordinate the graph has plotted the Y value on, but what the Y value actually is — by providingyValueForX(new Date(2018, 06, 26))
we should be able to get15
back, as that's already plotted on the graph?TL;DR: It should be possible to retrieve the "in-between" values, given an X-value within the domain for the graph