Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Smoothing arcs/plot points in D3.js/GeoJSON/TopoJSON/Shapefile (somewhere along the way)

I've been looking around a while for an answer to this, and I haven't been able to figure it out.

  • I'm ultimately creating a TopoJSON file from grid based data (GRIB files).
  • I can pretty easily interpolate the data down to a finer resolution grid so the plot points appear smoother when zoomed out, but when zoomed in, it's inevitable to see the blocky grid points.
  • I've also looked into simplification, which does help a bit but its not quite smoothing.
  • I'm using D3 to render the data.
  • Is this something that can be done on the front end or should/can it be done in the raw TopoJSON data?
  • I essentially don't want you to be able to tell that it's a grid, even if you zoom in 10,000%.
  • Here's an example of what I'm after:

enter image description here

like image 602
stewart715 Avatar asked Sep 09 '25 23:09

stewart715


2 Answers

Is this something that can be done on the front end or should/can it be done in the raw TopoJSON data?

This is something that should be done on the front end. If you were to smooth the data before you wrote it to the JSON file, the file would be needlessly big.

like image 72
Sam Avatar answered Sep 12 '25 12:09

Sam


If you're using D3.js, and you're working with lines, the built-in interpolate() function is the way to go.

Here is a working example of D3's line.interpolate() using "cardinal" smoothing:

http://codepen.io/gracefulcode/pen/doPmOK

example of D3's line.interpolate()

Here's the code:

var margin = {
    top: 30,
    right: 20,
    bottom: 30,
    left: 50
  },
  width = 600 - margin.left - margin.right,
  height = 270 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%d-%b-%y").parse;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x).orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y).orient("left").ticks(5);

// Define the line
var valueline = d3.svg.line()
  .interpolate("cardinal")
  .x(function(d) {
    return x(d.date);
  })
  .y(function(d) {
    return y(d.close);
  });

// Adds the svg canvas
var svg = d3.select("body").append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform",
    "translate(" + margin.left + "," + margin.top + ")");

// Get the data
d3.json('https://api.myjson.com/bins/175jl', function(error, data) {
  data.forEach(function(d) {
    d.date = parseDate(d.date);
    d.close = +d.close;
  });
  // Scale the range of the data
  // Starting with a basic graph 14
  x.domain(d3.extent(data, function(d) {
    return d.date;
  }));
  y.domain([0, d3.max(data, function(d) {
    return d.close;
  })]);
  // Add the valueline path.
  svg.append("path")
    .attr("class", "line")
    .attr("d", valueline(data));
  // Add the X Axis
  svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);
  // Add the Y Axis
  svg.append("g")
    .attr("class", "y axis")
    .call(yAxis);
});
like image 40
Grace Avatar answered Sep 12 '25 14:09

Grace