Need help coloring in countries from an array, leave rest default color

风流意气都作罢 提交于 2020-01-04 05:46:17

问题


I need some help taking the data from an array I created and then coloring only the countries that exist in the array in color, the rest of the countries that are not in the array I want left as default color. I am using D3 to do all of this, and I am pretty sure I can achieve what I need to via D3, but unsure how.

What I want to do, is take the country codes via data2 array, and based on their rank color them in dark red to green.

my array:

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}]

Currently, to color my map I just do

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

See the problem is, I am using one set of data to create the map, and want to use another set of data, data2, to color in the specific countries.

I tried the below but did not work.

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

Thank you in advance for anyone that helps.

I can post all my code if necessary, but I did not want to fill the page up unless needed,

FULL CODE:

    <!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>

回答1:


Ok, let's go step by step:

First, we'll grab the value for rank for each country in your data2 and we'll push that value in your GeoJSON, as a new property. Here is the code, that I got from Scott Murray (I see that in your code you use "topo" as a parameter name, but here I'm using "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
});

Check if you have ISOa3 in your Geojson, or any other country code to match country_code in your data2. Now we have a property named "color" in your GeoJSON, that matches "rank" in your data2. The next step is simple:

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

                    });

So, if there is a value for d.properties.color, it will fill according to var color = d3.scale.category10(). If there is no value, it will fill with #ccc, or whatever you want. Just to finish, if you want to fill them from red to green, dont use d3.scale.category10(). Instead, use:

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

For the colors, I recommend Cynthia Brewer's palette:

http://colorbrewer2.org/

EDIT: Only after reading the full code I realized that you are using TopoJSON. The code in my solution works for GeoJSON, not TopoJSON.




回答2:


You need to use a colour range using a scale, and not using ordinal values. You set the lower colour, the upper colour, and d3 works out where value d is between the min and max values and applies that percentage to work out the colour.

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

Take a look here: Gradient color in a treemap for D3



来源:https://stackoverflow.com/questions/35676105/need-help-coloring-in-countries-from-an-array-leave-rest-default-color

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!