【问题标题】:Need help coloring in countries from an array, leave rest default color需要帮助从数组中为国家/地区着色,保留默认颜色
【发布时间】:2016-06-11 02:35:43
【问题描述】:

我需要一些帮助,从我创建的数组中获取数据,然后仅对数组中存在的国家/地区进行着色,将数组中不存在的其他国家/地区保留为默认颜色。我正在使用 D3 来完成所有这些工作,并且我很确定我可以通过 D3 实现我所需要的,但不确定如何实现。

我想做的是通过 data2 数组获取国家代码,并根据它们的排名将它们着色为深红色到绿色。

我的数组:

var data2 = [{name:"United Kingdom", country_code: 826, rank: 1},{name:"United States", country_code: 840, rank: 2},{name:"Sweden", country_code: 752, rank: 3},
{name:"Canada", country_code: 124, rank: 4},{name:"Germany", country_code: 276, rank: 5},{name:"South Korea", country_code: 410, rank: 6},
{name:"Australia", country_code: 036, rank: 7},{name:"Italy", country_code: 380, rank: 8},
{name:"Ireland", country_code: 372, rank: 9},{name:"Jamaica", country_code: 388, rank: 10},
{name:"India", country_code: 356, rank: 11},{name:"Mexico", country_code: 484, rank: 12},{name:"France", country_code: 250, rank: 13},
{name:" Japan", country_code: 392, rank: 14},{name:"Finland", country_code: 246, rank: 15},
{name:"Spain", country_code: 724, rank: 16},{name:"Russia", country_code: 643, rank: 17},
{name:"Philippines", country_code: 608, rank: 18},{name:"Romania", country_code: 642, rank: 19},
{name:"Ukraine", country_code: 804, rank: 20}]

目前,我只是为我的地图着色

 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; });

看到问题是,我正在使用一组数据来创建地图,并且想使用另一组数据 data2 来为特定国家/地区着色。

我尝试了以下方法,但没有成功。

var color = d3.scale.category10();
feature.data(data2.features)
             .enter().append("path")
             .attr("class", "feature")
             .attr("d", path)
             .style("fill", function (d,i) { return color(i) });

提前感谢任何提供帮助的人。

如有必要,我可以发布所有代码,但除非需要,否则我不想填满页面,

完整代码:

    <!DOCTYPE html>
<meta charset="utf-8">
<title>D3 World Map Template | TechSlides</title>
<style>
.country:hover{
  stroke: #fff;
  stroke-width: 1.5px;
}
.text{
  font-size:10px;
  text-transform:capitalize;
}
#container {
  margin:10px 10%;
  border:2px solid #000;
  border-radius: 5px;
  height:100%;
  overflow:hidden;
  background: #F0F8FF;
}
.hidden { 
  display: none; 
}
div.tooltip {
  color: #222; 
  background: #fff; 
  padding: .5em; 
  text-shadow: #f5f5f5 0 1px 0;
  border-radius: 2px; 
  box-shadow: 0px 0px 2px 0px #a6a6a6; 
  opacity: 0.9; 
  position: absolute;
}
.graticule {
  fill: none;
  stroke: #bbb;
  stroke-width: .5px;
  stroke-opacity: .5;
}
.equator {
  stroke: #ccc;
  stroke-width: 1px;
}

</style>
</head>
<body>

  <h1>World Map Template with D3.js</h1>
  <p>Responsive D3 World Map with zoom and pan limits, graticule and equator lines, optimized TopoJSON with country names and colors, geo translation functions, geo coordinate plotting functions for points and text, and much more.  For a full list of features, please go <a href="http://techslides.com/d3-map-starter-kit/">back to article</a>.</p>


  <div id="container"></div>

<script src="js/d3.min.js"></script>
<script src="js/topojson.v1.min.js"></script>
<script>
d3.select(window).on("resize", throttle);

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


var width = document.getElementById('container').offsetWidth;
var height = width / 2;

var topo,projection,path,svg,g;

var graticule = d3.geo.graticule();

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

setup(width,height);

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

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

  svg = d3.select("#container").append("svg")
      .attr("width", width)
      .attr("height", height)
      .call(zoom)
      .on("click", click)
      .append("g");

  g = svg.append("g");

}

d3.json("data/world-topo-min.json", function(error, world) {

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

  topo = countries;
  draw(topo);

});

function draw(topo) {

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


  g.append("path")
   .datum({type: "LineString", coordinates: [[-180, 0], [-90, 0], [0, 0], [90, 0], [180, 0]]})
   .attr("class", "equator")
   .attr("d", path);


  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; });
  //offsets for tooltips
  var offsetL = document.getElementById('container').offsetLeft+20;
  var offsetT = document.getElementById('container').offsetTop+10;

  //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);
      }); 


  //EXAMPLE: adding some capitals from external CSV file
  d3.csv("data/country-capitals.csv", function(err, capitals) {

    capitals.forEach(function(i){
      addpoint(i.CapitalLongitude, i.CapitalLatitude, i.CapitalName );
    });

  });

}


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


function move() {

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


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

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

  zoom.translate(t);
  g.attr("transform", "translate(" + t + ")scale(" + s + ")");

  //adjust the country hover stroke width based on zoom level
  d3.selectAll(".country").style("stroke-width", 1.5 / s);

}



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


//geo translation on mouse click in map
function click() {
  var latlon = projection.invert(d3.mouse(this));
  console.log(latlon);
}


//function to add points and text to the map (used in plotting capitals)
function addpoint(lat,lon,text) {

  var gpoint = g.append("g").attr("class", "gpoint");
  var x = projection([lat,lon])[0];
  var y = projection([lat,lon])[1];

  gpoint.append("svg:circle")
        .attr("cx", x)
        .attr("cy", y)
        .attr("class","point")
        .attr("r", 1.5);

  //conditional in case a point has no associated text
  if(text.length>0){

    gpoint.append("text")
          .attr("x", x+2)
          .attr("y", y+2)
          .attr("class","text")
          .text(text);
  }

}

var data2 = [{name:"United Kingdom", country_code: 826, rank: 1},{name:"United States", country_code: 840, rank: 2},{name:"Sweden", country_code: 752, rank: 3},
{name:"Canada", country_code: 124, rank: 4},{name:"Germany", country_code: 276, rank: 5},{name:"South Korea", country_code: 410, rank: 6},
{name:"Australia", country_code: 036, rank: 7},{name:"Italy", country_code: 380, rank: 8},
{name:"Ireland", country_code: 372, rank: 9},{name:"Jamaica", country_code: 388, rank: 10},
{name:"India", country_code: 356, rank: 11},{name:"Mexico", country_code: 484, rank: 12},{name:"France", country_code: 250, rank: 13},
{name:" Japan", country_code: 392, rank: 14},{name:"Finland", country_code: 246, rank: 15},
{name:"Spain", country_code: 724, rank: 16},{name:"Russia", country_code: 643, rank: 17},
{name:"Philippines", country_code: 608, rank: 18},{name:"Romania", country_code: 642, rank: 19},
{name:"Ukraine", country_code: 804, rank: 20}];

    d3.json("world-map.json", function(json) {
                //Merge the rank in data2 and GeoJSON in a single array
                //Loop through once for each "rank" data value
                for (var i = 0; i < data2.length; i++) {
                    //Grab country name
                    var data2CountryCode = data2[i].country_code;
            console.log(data2CountryCode);
                    //Grab data value, and convert from string to float
                    var datarank = +data2[i].rank;
                        console.log(datarank);
                    //Find the corresponding country inside the GeoJSON
                    for (var j = 0; j < json.features.length; j++) {

                        //We'll check the official ISO country code
                        var jsonCountryCode = json.features[j].properties.un_a3;
                        console.log(jsonCountryCode);

                        if (data2CountryCode == jsonCountryCode) {

                            //Copy the data2 rank value into the GeoJSON, with the name "color"
                            json.features[j].properties.labelrank = datarank;

                            //Stop looking through the JSON
                            break;

                        }
                    }
                }

var color = d3.scale.quantize()
            .range(["Lime","GreenYellow","LawnGreen","LightGreen","LimeGreen","Green","DarkGreen","Yellow","Gold",
            "GoldenRod","DarkGoldenRod","Orange"    
,"DarkOrange","Coral","Red","OrangeRed","Tomato","Crimson","DarkRed","Brown"])
.domain([(d3.min(data2, function(d) { return d.rank; })),(d3.max(data2, function(d) { return d.rank; }))
]); 

               var country = g.selectAll(".country")
                   .data(json.features) //in my example, json.features
                   .enter()
                   .append("path")
                   .attr("d", path)
                   .style("fill", function(d) {

                        //Get data value
                        var value = d.properties.labelrank;

                        if (value) {
                            //If value exists…
                            return color(value);
                        } else {
                            //If value is undefined…
                            return "#ccc";
                        }

 });
}); 


</script>
</body>
</html>

【问题讨论】:

    标签: javascript d3.js colors


    【解决方案1】:

    好,我们一步一步来:

    首先,我们将在您的数据2 中获取每个国家/地区的 rank 值,并将该值作为新属性推送到您的 GeoJSON 中。这是我从 Scott Murray 那里得到的代码(我看到在你的代码中你使用“topo”作为参数名称,但这里我使用的是“json”):

            d3.json("yourgeojsonfile.json", function(json) {
    
                    //Merge the rank in data2 and GeoJSON in a single array
    
                    //Loop through once for each "rank" data value
                    for (var i = 0; i < data2.length; i++) {
    
                        //Grab country name
                        var data2CountryCode = data2[i].country_code;
    
                        //Grab data value, and convert from string to float
                        var dataValue = +data2[i].rank;
    
                        //Find the corresponding country inside the GeoJSON
                        for (var j = 0; j < json.features.length; j++) {
    
                            //We'll check the official ISO country code
                            var jsonCountryCode = json.features[j].properties.iso_a3;
    
                            if (data2CountryCode == jsonCountryCode) {
    
                                //Copy the data2 rank value into the GeoJSON, with the name "color"
                                json.features[j].properties.color = dataValue;
    
                                //Stop looking through the JSON
                                break;
    
                            }
                        }       
                    }
            //the rest of your code
    });
    

    检查您的 Geojson 中是否有 ISOa3,或任何其他国家/地区代码以匹配您的 data2 中的 country_code。 现在我们在 GeoJSON 中有一个名为“color”的属性,它与 data2 中的“rank”匹配。下一步很简单:

    var country = g.selectAll(".country")
                       .data(topo.features) //in my example, json.features
                       .enter()
                       .append("path")
                       .attr("d", path)
                       .style("fill", function(d) {
    
                            //Get data value
                            var value = d.properties.color;
    
                            if (value) {
                                //If value exists…
                                return color(value);
                            } else {
                                //If value is undefined…
                                return "#ccc";
                            }
    
                        });
    

    所以,如果 d.properties.color 有值,它将根据 var color = d3.scale.category10() 进行填充。如果没有值,它将用#ccc 填充,或者任何你想要的。 最后,如果您想将它们从红色填充为绿色,请不要使用 d3.scale.category10()。相反,使用:

    var color = d3.scale.quantize()
                .range([ //put your colors here as an array ])
                .domain([d3.min(data2, function(d) { return d.rank; }),
                         d3.max(data2, function(d) { return d.rank; })
    ]);
    

    对于颜色,我推荐 Cynthia Brewer 的调色板:

    http://colorbrewer2.org/

    编辑:只有在阅读了完整的代码后,我才意识到您正在使用 TopoJSON。我的解决方案中的代码适用于 GeoJSON,而不适用于 TopoJSON。

    【讨论】:

    • 我在上面添加了整个代码,但仍然出现一些错误。只是为了确认一下,我确实使用 JSON 来获取 world-topo.json 文件,所以我应该在上面的代码中使用 JSON,而不是 topo 对吗?我会继续搞砸它,我真的很感谢你的帮助!另外我使用的是联合国国家代码,不确定这是否适用于 iso_a3,请检查它们是否匹配。
    • 是的,它不需要是iso-a3,您只需要geojson中与您的data2国家代码匹配的属性即可。例如,关于英国,在您的 geojson 中查找编号为 826 的任何属性。然后,在我的代码中,只需将 properties.iso_a3 更改为 properties.foo。如果您确实编写了 function(json) 来加载 geojson,则必须在其余代码中编写 json.features,而不是 topo.features。
    • 哦,我明白了,现在您上传了完整的代码:删除 var = country 上方的 });。这是为了关闭 d3.json 函数,请注意它在我的示例中的注释“其余代码”之后。
    • 啊好吧我删除了它,认为它更接近工作。它抱怨没有定义缩放和油门,所以我删除了 em,但这仍然是一个错误 Uncaught TypeError: Cannot read property '0' of undefined on line 93。县代码在文件中称为“id”,所以我将其更改为该 properties.id 并且它也不喜欢 json.features.length 所以我将其设为 data2.length 我将更新 OP 中的完整代码
    【解决方案2】:

    您需要使用带有刻度的颜色范围,而不是使用序数值。您设置较低的颜色,较高的颜色,d3 计算出 d 值在最小值和最大值之间的位置,并应用该百分比来计算颜色。

    var colorScale = d3.scale.linear()
        .range(['lightgreen', 'darkgreen']) // or use hex values
        .domain([minValue, maxValue]);
    

    看这里:Gradient color in a treemap for D3

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多