4

I am trying to display a grid on a world map where each grid cell is filled with a color based on some data (e.g., temperature or humidity). I am trying to adapt the simple world map example here: http://techslides.com/demos/d3/d3-worldmap-boilerplate.html

I thought I might be able to use the built-in d3 graticule and add a fill color, like this:

g.append("path")
 .datum(graticule)
 .attr("class", "graticule")
 .attr("d", path)
 .style("fill", function(d, i) { return color(Math.floor((Math.random() * 20) + 1)); });

That doesn't work, though. Is there a way to fill in the grid cells generated by graticule? If not, what's the best way to go about overlaying a lat,long grid on the map with filled cells?

halfer
  • 19,824
  • 17
  • 99
  • 186
Samarth
  • 133
  • 1
  • 6
  • 1
    Something like [this](http://bl.ocks.org/jsl6906/c4e61fa2ed5006b1a290) which came from [this](http://stackoverflow.com/questions/25415885/d3-geo-spherical-arcs-rather-than-straight-lines-for-paralles) question. Cool stuff. – Mark Oct 13 '15 at 19:07
  • Mark, I saw that too. I think, going by that example, I would have to make a separate graticule for each cell. Is there a way to express that concisely in d3? – Samarth Oct 13 '15 at 19:38

2 Answers2

0

To do something like this, first create data set with all the N/S/E/W to define the limits.

var data set = [{W: -5.0, N: 50.0, E: 10.0, S: 40.0 }, {W: -95.0, N: 50.0, E: -40.0, S: 40.0 }];

Next post you load your world JSON add the path like this.

d3.json("http://techslides.com/demos/d3/data/world-topo.json", function(error, world) {

  var countries = topojson.feature(world, world.objects.countries).features;

  topo = countries;
  draw(topo);
  //iterate over the dataset created above for making paths.
  dataset.forEach(function(bb){
    var arc = d3.geo.graticule()
      .majorExtent([[bb.W, bb.S], [bb.E, bb.N]])
    //this will append the path to the g group so that it moves accordingly on translate/zoom   
    g.append("path")
        .attr("class", "arc")
        .attr("d", path(arc.outline()));

  });  
});

On Css add:

.arc {
  fill: red;[![enter image description here][1]][1]
  fill-opacity: 0.3;
  stroke: black;
  stroke-opacity: 0.5;
}

Full JS here:

d3.select(window).on("resize", throttle);

var zoom = d3.behavior.zoom()
    .scaleExtent([1, 8])
    .on("zoom", move);

var width = document.getElementById('container').offsetWidth-60;
var height = width / 2;
var dataset = [{W: -5.0, N: 50.0, E: 10.0, S: 40.0 }, {W: -95.0, N: 50.0, E: -40.0, S: 40.0 }];
var topo,projection,path,svg,g;

var tooltip = d3.select("#container").append("div").attr("class", "tooltip hidden");

setup(width,height);

function setup(width,height){
  projection = d3.geo.mercator()
    .translate([0, 0])
    .scale(width / 2 / Math.PI);

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

  svg = d3.select("#container").append("svg")
      .attr("width", width)
      .attr("height", height)
      .append("g")
      .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
      .call(zoom);
  g = svg.append("g");


}

d3.json("http://techslides.com/demos/d3/data/world-topo.json", function(error, world) {

  var countries = topojson.feature(world, world.objects.countries).features;

  topo = countries;
  draw(topo);
  dataset.forEach(function(bb){
    var arc = d3.geo.graticule()
      .majorExtent([[bb.W, bb.S], [bb.E, bb.N]])

    g.append("path")
        .attr("class", "arc")
        .attr("d", path(arc.outline()));

  });  
});

function draw(topo) {

  var country = g.selectAll(".country").data(topo);

  country.enter().insert("path")
      .attr("class", "country")
      .attr("d", path)
      .attr("id", function(d,i) { return d.id; })
      .attr("title", function(d,i) { return d.properties.name; })
      .style("fill", function(d, i) { return d.properties.color; });

  //ofsets plus width/height of transform, plsu 20 px of padding, plus 20 extra for tooltip offset off mouse
  var offsetL = document.getElementById('container').offsetLeft+(width/2)+40;
  var offsetT =document.getElementById('container').offsetTop+(height/2)+20;

  //tooltips
  country
    .on("mousemove", function(d,i) {
      var mouse = d3.mouse(svg.node()).map( function(d) { return parseInt(d); } );
        tooltip
          .classed("hidden", false)
          .attr("style", "left:"+(mouse[0]+offsetL)+"px;top:"+(mouse[1]+offsetT)+"px")
          .html(d.properties.name)
      })
      .on("mouseout",  function(d,i) {
        tooltip.classed("hidden", true)
      }); 

}

function redraw() {
  width = document.getElementById('container').offsetWidth-60;
  height = width / 2;
  d3.select('svg').remove();
  setup(width,height);
  draw(topo);
}

function move() {

  var t = d3.event.translate;
  var s = d3.event.scale;  
  var h = height / 3;

  t[0] = Math.min(width / 2 * (s - 1), Math.max(width / 2 * (1 - s), t[0]));
  t[1] = Math.min(height / 2 * (s - 1) + h * s, Math.max(height / 2 * (1 - s) - h * s, t[1]));

  zoom.translate(t);
  g.style("stroke-width", 1 / s).attr("transform", "translate(" + t + ")scale(" + s + ")");

}

var throttleTimer;
function throttle() {
  window.clearTimeout(throttleTimer);
    throttleTimer = window.setTimeout(function() {
      redraw();
    }, 200);
}

Image:

Image

halfer
  • 19,824
  • 17
  • 99
  • 186
Cyril Cherian
  • 32,177
  • 7
  • 46
  • 55
  • I would have loved to put the working code here ...but the world json is so large that plunk doesn't allow to load it, so if you need the full working code i can share it up on dropbox. – Cyril Cherian Oct 15 '15 at 00:21
0

I created d3-grid-map to solve a specific problem of placing sparse global 0.5 degree grid cells on a d3 map by drawing on canvas layers. It should support other grid sizes with some effort. It handles a couple of forms of javascript typed array inputs, but it could use more generalization.

Joseph Sheedy
  • 6,296
  • 4
  • 30
  • 31