Conditionally fill/color of voronoi segments

2019-05-26 22:55发布

I'm trying to conditionally color these voronoi segments based on the 'd.lon' value. If it's positive, I want it to be green, if it's negative I want it to be red. However at the moment it's returning every segment as green.

Even if I swap my < operand to >, it still returns green.

Live example here: https://allaffects.com/world/

Thank you :)

JS

// Stating variables
var margin = {top: 20, right: 40, bottom: 30, left: 45},
width = parseInt(window.innerWidth) - margin.left - margin.right; 
height = (width * .5) - 10;

var projection = d3.geo.mercator()
.center([0, 5 ])
.scale(200)
.rotate([0,0]);

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

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

var voronoi = d3.geom.voronoi()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.clipExtent([[0, 0], [width, height]]);

var g = svg.append("g");

// Map data
d3.json("/world-110m2.json", function(error, topology) {

// Cities data
d3.csv("/cities.csv", function(error, data) {
g.selectAll("circle")
   .data(data)
   .enter()
   .append("a")
              .attr("xlink:href", function(d) {
                  return "https://www.google.com/search?q="+d.city;}
              )
   .append("circle")
   .attr("cx", function(d) {
           return projection([d.lon, d.lat])[0];
   })
   .attr("cy", function(d) {
           return projection([d.lon, d.lat])[1];
   })
   .attr("r", 5)
   .style("fill", "red");

});

g.selectAll("path")
  .data(topojson.object(topology, topology.objects.countries)
      .geometries)
.enter()
  .append("path")
  .attr("d", path)
});

var voronoi = d3.geom.voronoi()
        .clipExtent([[0, 0], [width, height]]);

  d3.csv("/cities.csv", function(d) {
    return [projection([+d.lon, +d.lat])[0], projection([+d.lon, +d.lat]) [1]];
  }, function(error, rows) {
    vertices = rows;
      console.log(vertices);
      drawV(vertices);
    }
  );

      function polygon(d) {
          return "M" + d.join("L") + "Z";
      }

      function drawV(d) {
          svg.append("g")
            .selectAll("path")
            .data(voronoi(d), polygon)
           .enter().append("path")
            .attr("class", "test")
            .attr("d", polygon)

// This is the line I'm trying to get to conditionally fill the segment.
            .style("fill", function(d) { return (d.lon < 0 ? "red" : "green"     );} )
            .style('opacity', .7)
            .style('stroke', "pink")
            .style("stroke-width", 3);
      }

JS EDIT

d3.csv("/static/cities.csv", function(data) {
    var rows = [];
    data.forEach(function(d){
        //Added third item into my array to test against for color
        rows.push([projection([+d.lon, +d.lat])[0], projection([+d.lon, +d.lat]) [1], [+d.lon]])
    });

    console.log(rows); // data for polygons and lon value
    console.log(data); // data containing raw csv info (both successfully log)

    svg.append("g")
    .selectAll("path")
    .data(voronoi(rows), polygon)
    .enter().append("path")
    .attr("d", polygon)
  //Trying to access the third item in array for each polygon which contains the lon value to test
    .style("fill", function(data) { return (rows[2] < 0 ? "red" : "green" );} ) 
    .style('opacity', .7)
    .style('stroke', "pink")
    .style("stroke-width", 3)
});

标签: d3.js voronoi
1条回答
劫难
2楼-- · 2019-05-26 23:12

This is what's happening: your row function is modifying the objects of rows array. At the time you get to the function for filling the polygons there is no d.lon anymore, and since d.lon is undefined the ternary operator is evaluated to false, which gives you "green".

Check this:

var d = {};

console.log(d.lon < 0 ? "red" : "green");

Which also explains what you said:

Even if I swap my < operand to >, it still returns green.

Because d.lon is undefined, it doesn't matter what operator you use.

That being said, you have to keep your original rows structure, with the lon property in the objects.

A solution is getting rid of the row function...

d3.csv("cities.csv", function(data){
    //the rest of the code
})

... and creating your rows array inside the callback:

var rows = [];
data.forEach(function(d){
    rows.push([projection([+d.lon, +d.lat])[0], projection([+d.lon, +d.lat]) [1]])
});

Now you have two arrays: rows, which you can use to create the polygons just as you're using now, and data, which contains the lon values.

Alternatively, you can keep everything in just one array (just changing your row function), which is the best solution because it would make easier to get the d.lon values inside the enter selection for the polygons. However, it's hard providing a working answer without testing it with your actual code (it normally ends up with the OP saying "it's not working!").

查看更多
登录 后发表回答