简体   繁体   中英

Trying to get circles on map to transition into bars in bar chart with D3

I create a bubble map with D3, and I want the user to be able to click on a button and the circles on the map will transition into bars of a bar chart. I am using the enter, update, exit pattern, but right now what I have isn't working as the bar chart is drawn on top and all the bars and circles are translated, instead of the circles transitioning into bars and the bars being translated into place. Below is the relevant part of my code, and here is the link to the demo: https://jhjanicki.github.io/circles-to-bars/

var projection = d3.geo.mercator()
  .scale(150)
  .center([20, 40])
  .translate([width / 2, height / 2]);

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

 var features = countries2.features;   

 d3.csv("data/sum_by_country.csv", function(error, data) {

        data.sort(function(a,b){
                return a.value - b.value;
          });

        var myfeatures= joinData(features, data, ['value']);


        var worldMap = svg.append('g');
        var world = worldMap.selectAll(".worldcountries")
                .data(myfeatures)
                .enter()
                .append("path")
                .attr("class", function(d){
                    return "World " + d.properties.name+" worldcountries";
                })
                .attr("d", path)
                .style("fill", "#ddd")
              .style("stroke", "white")
              .style("stroke-width", "1");

        var radius =  d3.scale.sqrt()
                .domain([0,1097805])
                .range([3, 20]);

        var newFeatures = [];
        myfeatures.forEach(function(d){
            if(d.properties.hasOwnProperty("value")){
                console.log(d.properties.name);
                newFeatures.push(d);
            }

        });


        newFeatures.sort(function(a,b){
                return b.properties.value - a.properties.value;
          });


        var bubbles = svg.append("g").classed("bubbleG","true");

        bubbles.selectAll("circle")
            .data(newFeatures)
            .enter().append("circle")
            .attr("class", "bubble")
            .attr("transform", function(d) { 
                return "translate(" + path.centroid(d) + ")"; 
            })
            .attr("r", function(d){
                return radius(d.properties.value);
            })
            .attr("fill","#2166ac")
            .attr("stroke","white")
            .attr("id", function(d){
                return "circle "+d.properties.name;
            });


             $('#bubblebar').click(function(){
                mapBarTransition(newFeatures,bubbles)
            });

    });




// button onclick
function mapBarTransition(data,bubbles){

        var margin = {top:20, right:20, bottom:120, left:80},
        chartW = width - margin.left - margin.right,
        chartH = height - margin.top - margin.bottom;

        var x = d3.scale.ordinal()
                .domain(data.map(function(d) { return d.properties.name; }))
                .rangeRoundBands([0, chartW], .4);
        var y = d3.scale.linear()
                .domain([0,1097805])
                .nice()
                .range([chartH,0]);

        var xAxis = d3.svg.axis()
                    .scale(x)
                    .orient("bottom");

        var yAxis = d3.svg.axis()
                    .scale(y)
                    .ticks(8)
                    .orient("left");

        var barW = width / data.length;


                bubbles.append("g").classed("bubblebar-chart-group", true);
                bubbles.append("g").classed("bubblebar-x-axis-group axis", true);
                bubbles.append("g").classed("bubblebar-y-axis-group axis", true);

            bubbles.transition().duration(1000).attr({transform: "translate(" + margin.left + "," + margin.top + ")"});

            bubbles.select(".bubblebar-x-axis-group.axis")
                .attr({transform: "translate(0," + (chartH) + ")"})
                .call(xAxis)
                .selectAll("text")  
                .style("text-anchor", "end")
                .attr("dx", "-.8em")
                .attr("dy", ".15em")
                .attr("transform", function(d) {
                    return "rotate(-65)" 
                    });


            bubbles.select(".bubblebar-y-axis-group.axis")
                .transition()
                .call(yAxis);


            barW = x.rangeBand();

            var bars = bubbles.select(".bubblebar-chart-group")
                    .selectAll(".bubble")
                    .data(data);

            bars.enter().append("rect")
                .classed("bubble", true)
                .attr("x", function(d) { return x(d.properties.name); })
                .attr("y", function(d) { return y(d.properties.value); })
                .attr("width", barW)
                .attr("height", function(d) { return chartH - y(d.properties.value); })
                .attr("fill","#2166ac");


            bars.transition()
                .attr("x", function(d) { return x(d.properties.name); })
                .attr("y", function(d) { return y(d.properties.value); })
                .attr("width", barW)
                .attr("height", function(d) { return chartH - y(d.properties.value); });


            bars.exit().transition().style({opacity: 0}).remove();


}

And here is the repo for your reference: https://github.com/jhjanicki/circles-to-bars

First, you have two very different selections with your circles and bars. They are in separate g containers and when you draw the bar chart, you aren't even selecting the circles.

Second, I'm not sure that d3 has any built-in way to know how to transition from a circle element to a rect element. There's some discussion here and here

That said, here's a quick hack with your code to demonstrate one way you could do this:

 <!DOCTYPE html> <head> <meta charset="utf-8"> <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" rel='stylesheet'> <link href="//rawgit.com/jhjanicki/circles-to-bars/master/css/style.css" rel='stylesheet'> <!-- Roboto & Asar CSS --> <link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'> <link href="https://fonts.googleapis.com/css?family=Asar" rel="stylesheet"> </head> <body> <button type="button" class="btn btn-primary" id="bubblebar">Bar Chart</button> <div id="chart"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> <!-- D3.geo --> <script src="https://d3js.org/d3.geo.projection.v0.min.js"></script> <!-- jQuery --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <script src="//rawgit.com/jhjanicki/circles-to-bars/master/data/countries2.js"></script> <script> window.onload = function() { // set up svg and scrolling var width = window.innerWidth / 2; var height = window.innerHeight; var svg = d3.select("#chart").append("svg") .attr('width', width) .attr('height', height); var bubbleMapState = 'map'; // map or bar var projection = d3.geo.mercator() .scale(150) .center([20, 40]) .translate([width / 2, height / 2]); var path = d3.geo.path() .projection(projection); var features = countries2.features; d3.csv("//rawgit.com/jhjanicki/circles-to-bars/master/data/sum_by_country.csv", function(error, data) { data.sort(function(a, b) { return a.value - b.value; }); var myfeatures = joinData(features, data, ['value']); var worldMap = svg.append('g'); var world = worldMap.selectAll(".worldcountries") .data(myfeatures) .enter() .append("path") .attr("class", function(d) { return "World " + d.properties.name + " worldcountries"; }) .attr("d", path) .style("fill", "#ddd") .style("stroke", "white") .style("stroke-width", "1"); var radius = d3.scale.sqrt() .domain([0, 1097805]) .range([3, 20]); var newFeatures = []; myfeatures.forEach(function(d) { if (d.properties.hasOwnProperty("value")) { console.log(d.properties.name); newFeatures.push(d); } }); newFeatures.sort(function(a, b) { return b.properties.value - a.properties.value; }); var bubbles = svg.append("g").classed("bubbleG", "true"); bubbles.selectAll("rect") .data(newFeatures) .enter().append("rect") .attr("class", "bubble") .attr("transform", function(d) { return "translate(" + path.centroid(d) + ")"; }) .attr("width", function(d) { return radius(d.properties.value) * 2; }) .attr("height", function(d){ return radius(d.properties.value) * 2; }) .attr("rx", 20) .attr("fill", "#2166ac") .attr("stroke", "white") .attr("id", function(d) { return "circle " + d.properties.name; }); $('#bubblebar').click(function() { mapBarTransition(newFeatures, bubbles) }); }); // button onclick function mapBarTransition(data, bubbles) { if (bubbleMapState == 'map') { bubbleMapState == 'bar'; } else { bubbleMapState == 'map'; } var margin = { top: 20, right: 20, bottom: 120, left: 80 }, chartW = width - margin.left - margin.right, chartH = height - margin.top - margin.bottom; var x = d3.scale.ordinal() .domain(data.map(function(d) { return d.properties.name; })) .rangeRoundBands([0, chartW], .4); var y = d3.scale.linear() .domain([0, 1097805]) .nice() .range([chartH, 0]); var xAxis = d3.svg.axis() .scale(x) .orient("bottom"); var yAxis = d3.svg.axis() .scale(y) .ticks(8) .orient("left"); var barW = width / data.length; bubbles.append("g").classed("bubblebar-chart-group", true); bubbles.append("g").classed("bubblebar-x-axis-group axis", true); bubbles.append("g").classed("bubblebar-y-axis-group axis", true); bubbles.transition().duration(1000).attr({ transform: "translate(" + margin.left + "," + margin.top + ")" }); bubbles.select(".bubblebar-x-axis-group.axis") .attr({ transform: "translate(0," + (chartH) + ")" }) .call(xAxis) .selectAll("text") .style("text-anchor", "end") .attr("dx", "-.8em") .attr("dy", ".15em") .attr("transform", function(d) { return "rotate(-65)" }); bubbles.select(".bubblebar-y-axis-group.axis") .transition() .call(yAxis); barW = x.rangeBand(); var bars = d3.select(".bubbleG") .selectAll(".bubble") .data(data); bars.enter().append("rect") .classed("bubble", true) .attr("x", function(d) { return x(d.properties.name); }) .attr("y", function(d) { return y(d.properties.value); }) .attr("width", barW) .attr("height", function(d) { return chartH - y(d.properties.value); }) .attr("fill", "#2166ac"); bars.transition() .duration(1000) .attr("transform", function(d){ return "translate(" + x(d.properties.name) + "," + y(d.properties.value) + ")"; }) .attr("rx", 0) .attr("width", barW) .attr("height", function(d) { return chartH - y(d.properties.value); }); bars.exit().transition().style({ opacity: 0 }).remove(); } function joinData(thisFeatures, thisData, DataArray) { //loop through csv to assign each set of csv attribute values to geojson counties for (var i = 0; i < thisData.length; i++) { var csvCountry = thisData[i]; //the current county var csvKey = csvCountry.Country; //the CSV primary key //loop through geojson regions to find correct counties for (var a = 0; a < thisFeatures.length; a++) { var geojsonProps = thisFeatures[a].properties; //the current region geojson properties var geojsonKey = geojsonProps.name; //the geojson primary key //where primary keys match, transfer csv data to geojson properties object if (geojsonKey == csvKey) { //assign all attributes and values DataArray.forEach(function(attr) { var val = parseFloat(csvCountry[attr]); //get csv attribute value geojsonProps[attr] = val; //assign attribute and value to geojson properties }); }; }; }; return thisFeatures; }; } </script> </body> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM