How do make my plot points smooth in d3.js using projection?

3.6k views Asked by At

I'm able to plot some weather data onto a map using the following code. However the points are rectangles and i'd like to get them smoother.

Rectangles Plotted,

I'd like to plot them smoother like something similar to Ideal plot points

I believe I need to look into interpolating, spatial analysis, and/or Choropleth maps. I think they are different algorithms in doing this. I feel like i need to fill in more points in between the existing ones? And with that is it possible to make gradient like points? Is this doable in D3? Or should i consider using three.js or WebGL stuff?

var width = 960,
height = 960;

var map = {};
var projection = d3.geo.mercator()
.scale((width + 1) / 2 / Math.PI)
.translate([width / 2, height / 2])
.precision(.1);

var path = d3.geo.path()
.projection(projection);

var graticule = d3.geo.graticule();

var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);

svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);

d3.json("world-50m.json", function(error, world) {
 svg.insert("path", ".graticule")
  .datum(topojson.feature(world, world.objects.land))
  .attr("class", "land")
  .attr("d", path);

svg.insert("path", ".graticule")
  .datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
  .attr("class", "boundary")
  .attr("d", path);
});

map.plot_points = [];
map.max = 30;
map.min = -1;
var opacity = d3.scale.linear()
 .domain([map.min, map.max])
 .range([0,1]);  
var rainbow = ["#CE0C82", "#800CCE", "#1F0CCE", "#0C5BCE", "#0C99CE", "#2ECE0C", "#BAE806", "#FEFF00", "#FFCD00", "#FF9A00", "#FF6000", "#FF0000"];
zs.forEach(function(zv,zi){
    zv.forEach(function(zzv, zzi){
        if(zzv != 999)
            {
                map.plot_points.push({lat: ys[zi], long:xs[zzi],value:zzv});
            }

        })
});
console.log(map);
var points = svg.selectAll("rects.points")
 .data(map.plot_points)
 .enter()
 .append("rect")
 .attr("class", "points")
 .style("fill", function(d) {
   var scale = d3.scale.linear().domain([map.min, map.max]).range([1, rainbow.length]);
        return rainbow[Math.round(scale(d.value))]; 
}).attr("width", 8)
.attr("height", 8)
.style("fill-opacity", 1)
.attr("transform", function(d) {
        return "translate(" + projection([d.long, d.lat]) + ")";
})
4

There are 4 answers

0
Xumo On

Maybe you could take a look into heatmap js.

http://www.patrick-wied.at/static/heatmapjs/

Although is point based it may give you a hint. It uses canvas instead of svg.

2
Lars Kotthoff On

It sounds like the problem in your case is the data. What you would need to do is take the original data and interpolate it to a smoother form. For this, you can use a GIS program such as QGIS. How exactly to do that depends on what format your original data is in.

Once you have the smoother data, you can plot it again in D3. My guess is that the end result would be somewhat similar to what I've done here, where contour lines are drawn to much the same effect as what you're aiming for.

2
Roger Veciana On

Jason Davies wrote an implementation of the conrec algorithm that does exactly what you need:

https://github.com/jasondavies/conrec.js It's got a working example inside

0
tomtomtom On

I believe that the white stripes are happening because of the projection you are using; In fact the height of each rectangle should adjust accordingly going north and south from the Equator, because the Mercator projection alters the distance going north and south.
To have a fixed height of the rectangles you could try with this projection instead:
http://bl.ocks.org/mbostock/3757119
which preserves the dimension going north and south