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().