I've made a chart similar to the one pictured here.
Of course d3 can create the y axis, but it turns out that d3 can also give us the logic for making these arcs. Example snippet below:
var dScale = d3.scaleLinear()
.domain([-90,90])
.range([0,Math.PI]);
let arc = d3.arc()
.innerRadius(0)
.outerRadius(function(d) {return yScale(d.y)})
.startAngle(function(d) {return dScale(d.top)})
.endAngle(function(d) {return dScale(d.bottom)});
However, creating an "axis" for these degrees (dScale()) in my snippet seems way harder -- to the point where it doesn't feel like d3 anymore.
Question
Unless I'm mistaken, we'd have to get real hacky at this point and hardcode lines, ticks and axis labels using dScale, but is anyone able to help me see a more "d3-ic" (analog of pythonic?) way of achieving a degree scale?
Related
I have been working on a D3 Zoombable Sunburst based on the following example: https://observablehq.com/#d3/zoomable-sunburst.
From the modifications I have made, the diagram is near to my requirements. However, I am having issues in forcing the first inner ring to have a fixed thickness. As I understand it, the sizing is calculated based on the JSON data.
I have looked at the following StackOverflow post as a starting point: D3 Sunburst. How to set different ring\level widths but the section of code I need to modify differs as the Zoomable Sunburst is using different arc variables. Being new to D3's I am not to sure why these differ.
The code I need to rework is where the inner and outer radius's are getting set:
var arc = d3.arc()
.startAngle(d => d.x0)
.endAngle(d => d.x1)
.padAngle(d => Math.min((d.x1 - d.x0) / 2, 0.005))
.padRadius(radius * 2.5)
.innerRadius(d => d.y0 * radius)
.outerRadius(d => Math.max(d.y0 * radius, d.y1 * radius - 1));
On a final point - How can I ensure these calculations are retained when the user clicks through different layers? Based on the various tweaks I was testing in an attempt to fix the thickness of a ring, the original calculations are wiped.
Any help is appreciated.
I am trying to create something like this geology visual below that maps soil by its composition (of a given three components):
To my knowledge there is only d3.axisBottom() but nothing like d3.axis45Degrees(). So unfortunately my attempts at this visual were cut short virtually right off the bat because I can't even figure out how to set up the axes for a triangular coordinate plane.
Question
Can d3 handle such an axis configuration, or are there any other d3 methods that would be relevant for a task such as this?
This question is borderline "too broad". However, I believe that it's an interesting question, since the documentation may lead someone to believe that only vertical/horizontal axes are possible.
You can always rotate the axis (any axis, be it axisBottom, axisTop, axisRight or axisLeft) and rotate the <text> elements back.
Here is a simple demo (full of magic numbers):
const svg = d3.select("svg");
const scale = d3.scaleLinear([10, 380]);
const axis = d3.axisLeft(scale);
const axis2 = d3.axisRight(scale);
const axis3 = d3.axisBottom(scale);
const axisGroup = svg.append("g")
.attr("transform", "rotate(30, 100, 400)")
.call(axis)
.selectAll("text")
.attr("transform", "rotate(-30, -10, 0)");
const axisGroup2 = svg.append("g")
.attr("transform", "rotate(-30, 108, -378)")
.call(axis2)
.selectAll("text")
.attr("transform", "rotate(30, 10, 0)");
const axisGroup3 = svg.append("g")
.attr("transform", "translate(14,333)")
.call(axis3)
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg width="450" height="400"></svg>
Change those magic numbers accordingly. By the way, in that triangle you shared in your question the internal angles are 60 degrees (not 45), so here I'm rotating the axes by 30 degrees.
Finally, it's worth mentioning that I just transformed (translate, rotate etc...) those axes. For a real plot, like the one in your image, you'll have to create a whole math just to calculate where in the SVG the values of the 3 coordinates will fall.
I have a scatterplot that uses constraint relaxation to de-conflict the labels for the points that it graphs (Plunker here). The problem is that, when I relax the constraints, this causes collisions between the point labels and the x-axis labels. The axes are generated using d3.extent and d3.scale.linear.
I've tried to de-conflict the point labels and the x-axis by extending the length of the y-axis, but the closest I've come to achieving this is by changing the original value of 0 to 30 in the following stanza:
var yext = d3.extent(data, d => d[1]);
var sy = d3.scale.linear()
.domain(yext)
.range([height, 30]) // flip y-axis
.nice();
The result is less than ideal, leaving an awkward gap instead of an intersection between the x and y axes:
What I want to achieve is something like this:
(Except I want to achieve this through code, rather than Photoshop).
Can anyone demonstrate a solution? (Plunker here)
Why don't you add a padding in the domain? Like:
.domain([yext[0] * 0.95, yext[1] * 1.05])
//less here---------^-- more here----^
Here is the plunker with that solution: http://plnkr.co/edit/rKArjn7DwQa9g1X5CaNW?p=preview
I'm just starting out with D3 and am quickly understanding that it's a pretty low level tool.
I'm using D3 to produce a Marimekko chart using this great example by Mike Bostock in
b.locks, which is in all honestly a way too advanced place to start for me, but I started using D3 because I need a Marimekko chart, so here I am.
The x-axis here has ticks, 0 to 100% with 10% intervals. If my understanding of these code excerpts is correct...
Set the x axis to a linear scale
var x = d3.scale.linear().range([0, width - 3 * margin]);
Give the x-axis 10 ticks
var xtick = svg.selectAll(".x").data(x.ticks(10))
In my usage case , I'd like to have the x-axis ticks at the irregular intervals inherent to a Marimekko chart, and the axis labels to be the category, rather than a percentage.
The desired behaviour, as far as x-axis labelling, can be illustrated by this b.locks example by 'cool Blue'
I've got as far as understanding that I need a ordinal axis rather than a linear one (as in this excerpt of cool Blue's code)
var padding = 0.1, outerPadding = 0.3,
x = d3.scale.ordinal().rangeRoundBands([0, width], padding, outerPadding);
How can I modify Mike Bostock's code to give me an example where the x-axis ticks label the column (ideally centrally), as opposed to providing a %age of the width?
I wouldn't say that D3 is that low level, since it has a lot of abstractions. However, D3 is not a charting tool (and, in that sense, it is low level...), which makes things hard for a beginner.
However, you're lucky: the changes needed here are minimal. First, you'll pass the correct data to the selection that generates the axis...
var xtick = svg.selectAll(".x")
.data(segments)
//etc..
... and then use the same math for the translate, but adding half the sum:
.attr("transform", function(d, i) {
return "translate(" + x((d.offset + d.sum / 2) / sum) + "," + y(1) + ")";
});
Of course, you'll print the key, not a percentage:
xtick.text(function(d) {
return d.key
});
Here is the updated bl.ocks: https://bl.ocks.org/anonymous/09a8881e5bab2b12e7fd46c90a63b3fd/fd7b1a7b20f8436666f1544b6774778e748934ba
I can take a set of triplets [X,Y,Z] and immediately generate a (smooth) contour plot using Python and matplotlib with a single call to tricontour(). One can also generate contours 'easily' using plot.ly, but I find it to be unacceptably slow. (Also, I'm not interested in the MATLAB solution, which is similar to the Python)
I'm looking for similar functionality using d3.js. I would settle for a "surface plot" instead of contours, or a "heat map" without contour lines.
I can see how to generate a colored Delaunay triangulation and/or a colored Voronoi Tesselation, but the question of how to generate a contour plot in d3 from irregular data points seems to still be an open one (even though the question on this was prematurely closed!).
So far, all I've seen are approaches "by hand", using Radial basis functions (gaussian blur) or grid interpolation using Barycentric interpolation.
I'd even be willing to 'live with' Gouraud-shading or Coon-gradients on a Delaunay triangulation, but apparently "advanced shading methods" like Gourand or Coon gradients are not in "regular" SVG but are proposed for SVG2...not sure where that leaves me with d3 & (regular) SVG. It seems like doing this SVG gradient-shading by hand would be a major pain.
Is there a "better" package-y way to do this, i.e. something that doesn't require so much 'custom' code? (Maybe via some multidimensional Bezier routine I haven't found yet?)
I'll post a Fiddle with my starting point: a colored Voronoi tesselation: https://jsfiddle.net/k2v2jy7s/1/. Can you help me take this from "blocky" to "smooth" (and maybe even show contour lines)?
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var npoints = 1000;
var sites = d3.range(npoints)
.map(function(d) { return [Math.random() * width, Math.random() * height]; });
// values at data points / colors being mapped = "zvals"
var kx = 3.14159/(width*0.5);
var ky = 3.14159/(height*0.5);
var zvals = d3.range(npoints)
for (i = 0; i < npoints; i++) {
zvals[i] = (1.0 + Math.cos(kx*sites[i][0]) * Math.cos(ky*sites[i][1]))/2.0;
zvals[i] *= zvals[i];
}
var g = svg.append("g")
.attr("transform", "translate(" + 0+ "," + 0 + ")");
var voronoi = d3.voronoi()
.extent([[-1, -1], [width + 1, height + 1]]);
var polygon = svg.append("g")
.attr("class", "polygons")
.selectAll("path")
.data(voronoi.polygons(sites))
.enter().append("path")
.style('fill', function(d,i){ return d3.hsl( zvals[i]*310, 1, .5); })
.call(redrawPolygon);
function redrawPolygon(polygon) {
polygon
.attr("d", function(d) { return d ? "M" + d.join("L") + "Z" : null; });
}
</script>
Update: Also found this blocks.org post on "Gradient Heatmaps", which as I mentioned is the sort of result I'd be willing to live with, but again that's a large quantity of custom code. Would really prefer a compact 'stock' solution, a la tricontour().
5 ½ years, and no answers to this question!
Well, I've also been looking into how to generate contours from a series of [X,Y,Z] points in Javascript, but have not yet found the best or most complete solution. A lot of solutions I came across via Googling (such as d3-contour) are designed for an evenly spaced grid of values, not an irregular series of points as you might obtain from a land survey.
d3-tricontour
The d3-tricontour library looks perhaps the most promising, though, so I might have a play around with it.
Here's an example of what it can generate:
(The labels are optional.)
Apparently it uses the delaunay and meandering triangles algorithms to convert arbitrary points into triangles and then contour geometry. The algorithm works in O(n) where n is the number of edges, meaning it's very fast and scales perfectly well.
To learn more you can visit their:
Github repository
Many examples on Observable
Alternatives
Otherwise, there might be other ways to do this. If working with one of the grid-based libraries, I think the general process would be to:
Convert arbitary [X,Y,Z] points into a grid — the Delaunay algorithm is probably a great place to start (see d3-delaunay or other delaunay libraries)
Find the Z value for each point in the grid using some kind of interpolation (the maths for that, I'm not sure about)
Then feed that result into one of the grid-based contouring libraries
Constraining Contours
Also take note that creating contours from real world terrain also requires "constraining" some edges so that contours don't crossover ridgelines where they shouldn't.
CDT-JS is a library web app (with no separate library available as yet) that calculates constrained Delaunay triangulation, which might be useful for this case.
Otherwise, in theory, you might be able to create this kind of functionality by injecting additional [X,Y,Z] points along your lines of contraint prior to rendering. But I haven't tested this approach.