在D3中使用不同数据集更新数据的问题

时间:2019-01-15 14:32:36

标签: javascript d3.js

我在D3中是新手。我正在尝试根据一组国家绘制棒棒糖图表。在html页面上,我放置了一些按钮,允许用户根据国家所属的大陆来更改国家/地区的选择。

加载页面后,图表已成功呈现,但单击按钮后出现错误。

例如,当我单击“亚洲”按钮时,图表可以显示该线,但圆圈缺失。

当我单击“概述”按钮并尝试查看国家的概述时,图表仅显示了我刚才看到的亚洲国家/地区的线。其他国家的电话不见了,他们的圈子也没了。

我正在遵循this example尝试使用.enter和.exit函数更新图表,但如示例所示,我无法更新图表。

数据集:

par_code,continent,country,date_1,camp_1,date_2,camp_2,date_3,camp_3
P027,Asia,China,09/05/2018,BBB,14/12/2018,AAA,,
P030,Asia,India,09/05/2018,BBB,18/09/2018,AAA,26/10/2018,CCC
P038,North America,United States,09/05/2018,BBB,14/12/2018,AAA,,
P042,Asia,Indonesia,09/05/2018,BBB,14/12/2018,AAA,,
P056,South America,Brazil,09/05/2018,BBB,14/12/2018,AAA,,
P058,Asia,Pakistan,09/05/2018,BBB,24/06/2018,AAA,28/11/2018,CCC
P059,Africa,Nigeria,09/05/2018,BBB,27/06/2018,AAA,,
P092,Asia,Bangladesh,09/05/2018,BBB,14/12/2018,AAA,,
P115,Europe,Russia,09/05/2018,AAA,13/05/2018,CCC,,
P134,Asia,Japan,09/05/2018,BBB,01/07/2018,AAA,13/12/2018,CCC
P154,North America,Mexico,09/05/2018,BBB,14/12/2018,AAA,,
P166,Asia,Philippines,09/05/2018,BBB,26/10/2018,FFF,,
P167,Asia,Vietnam,09/05/2018,BBB,12/12/2018,AAA,,
P168,Africa,Ethiopia,09/05/2018,BBB,25/10/2018,DDD,,
P170,Europe,Germany,09/05/2018,BBB,10/05/2018,EEE,,
P176,Africa,Egypt,09/05/2018,BBB,19/09/2018,AAA,,
P177,Asia,Iran,09/05/2018,BBB,12/12/2018,AAA,,
P180,Asia,Turkey,09/05/2018,AAA,25/10/2018,DDD,,
P182,Africa,Democratic Republic of the Congo,09/05/2018,BBB,25/10/2018,DDD,,
P183,Asia,Thailand,09/05/2018,BBB,12/12/2018,AAA,,
P184,Europe,France,09/05/2018,BBB,12/12/2018,AAA,,
P193,Europe,United Kingdom,09/05/2018,BBB,12/06/2018,CCC,,
P194,Europe,Italy,09/05/2018,BBB,12/06/2018,CCC,,
P197,Asia,Myanmar,09/05/2018,BBB,12/06/2018,CCC,,
P199,Africa,South Africa,09/05/2018,BBB,12/06/2018,CCC,,
P200,Asia,South Korea,09/05/2018,BBB,12/06/2018,CCC,,
P201,Europe,Spain,09/05/2018,BBB,12/06/2018,CCC,,
P202,Europe,Ukraine,09/05/2018,BBB,12/06/2018,CCC,,
P203,South America,Colombia,09/05/2018,AAA,12/05/2018,CCC,,
P204,Africa,Tanzania,09/05/2018,BBB,12/06/2018,CCC,,
P206,Africa,Sudan,09/05/2018,BBB,12/06/2018,CCC,,
P207,Africa,Kenya,09/05/2018,BBB,12/06/2018,CCC,,
P209,South America,Argentina,09/05/2018,AAA,12/05/2018,CCC,,
P210,Europe,Poland,09/05/2018,BBB,12/06/2018,CCC,,
P213,Africa,Algeria,09/05/2018,BBB,12/06/2018,CCC,,
P215,North America,Canada,09/05/2018,BBB,12/06/2018,CCC,,
P216,Asia,Iraq,09/05/2018,BBB,12/06/2018,CCC,,
P217,Africa,Morocco,09/05/2018,BBB,12/06/2018,CCC,,
P218,Africa,Uganda,09/05/2018,BBB,12/06/2018,CCC,,
P220,South America,Peru,09/05/2018,BBB,12/06/2018,CCC,,
P221,South America,Venezuela,09/05/2018,BBB,12/06/2018,CCC,,
P222,Asia,Malaysia,09/05/2018,BBB,12/06/2018,CCC,,

我用于更新图表的HTML页面上的按钮:

<button onclick="updateChart('Overview')">Overview</button>
<button onclick="updateChart('Asia')">Asia</button>
<button onclick="updateChart('Africa')">Africa</button>
<button onclick="updateChart('Europe')">Europe</button>
<button onclick="updateChart('North America')">North America</button>
<button onclick="updateChart('South America')">South America</button>

完整的js代码:

const margin = { top: 20, right: 20, bottom: 50, left: 85 };
const width = 900;
const height = 700;

const svg = d3.select('#lollipop')
.append('svg')
.attr('width', width)
.attr('height', height)
.attr('viewBox', '0 0 ' + Math.min(width, height) + ' ' + Math.min(width, 
height))
.attr('preserveAspectRatio', 'xMinYMin')

const g = svg.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`);

const parseTime = d3.timeParse("%d/%m/%Y");

const xScale = d3.scaleTime()
.range([0, (width - margin.right - margin.left)]);

const yScale = d3.scaleBand()
.range([0, (height - margin.bottom - margin.top)])
.padding(1);

function x_gridlines() {
return d3.axisTop(xScale)
.ticks(5)
}

const beginDate = parseTime("01/05/2018");
let transTime = 100;
let duration;
let latestDate;
let dates = [];
let parsedDates;

d3.csv('../data/lollipop.csv')
.then(data => {

data.forEach(d => {
    d.continent = d.continent;
    d.country = d.country;

    d.camp_1 = d.camp_1;

    d.camp_2 = d.camp_2;

    d.camp_3 = d.camp_3;

    // determine latest date for x-axis
    for (let i = 1; i <= 3; i++) {
        const date = `date_${i}`;

        if (d[date] !== '') {
            if (dates.indexOf(d[date]) === -1) {
                dates.push(d[date]);
            }
        }
    }

    d.date_1 = parseTime(d.date_1);
    d.date_2 = parseTime(d.date_2);
    d.date_3 = parseTime(d.date_3);

    if (d.date_3) {
        duration = d.date_3 - d.date_1;
    } else {
        duration = d.date_2 - d.date_1;
    }

    d.duration = duration;
});

parsedDates = dates.map(date => parseTime(date));

latestDate = d3.max(parsedDates);

// sort MP list according to duration
data.sort((a, b) => {
    return a.duration - b.duration;
})

let jumpList = data.map(d => d.country);

console.log(jumpList);

xScale.domain([beginDate, latestDate]);
yScale.domain(jumpList);

// grid
g.append('g')
    .attr('class', 'grid')
    .attr('transform', 'translate(0,0)')
    .call(x_gridlines()
        .tickSize(-height + margin.bottom + margin.top)
        .tickFormat('')
    )

// define axes
g.append('g')
    .attr('class', 'axis, x-axis')
    .call(d3.axisTop(xScale)
        .ticks(5)
        .tickFormat(d3.timeFormat('%b %Y')));

g.append('g')
    .attr('class', 'axis, y-axis')
    .call(d3.axisLeft(yScale));

// add lines
const timeline = g.selectAll('.timeline')
    .data(data, d => d.par_code);

timeline.enter()
    .append('line')
    .attr('class', 'timeline')
    .attr('x1', d => xScale(d.date_1))
    .attr('y1', d => yScale(d.country))
    .attr('x2', d => {
        if (d.date_3) {
            return xScale(d.date_3);
        } else if (d.date_2) {
            return xScale(d.date_2);
        }
    })
    .attr('y2', d => yScale(d.country))
    .attr('stroke', 'grey');

// add circles
for (let i = 1; i <= 3; i++) {
    const date = `date_${i}`;
    const camp = `camp_${i}`;

    g.selectAll('.circle')
        .data(data, d => d.par_code)
        .enter()
        .append('circle')
        .attr('class', 'time-circle')
        .attr('cx', d => xScale(d[date]))
        .attr('cy', d => yScale(d.country))
        .attr('r', '3')
        .style('fill', d => campColor(d[camp]));
}
})

function updateChart(area) {

d3.csv('../data/lollipop.csv')
.then(data => {

    if (area === 'Asia') {
        data = data.filter(d => d.continent === 'Asia');
    } else if (area === 'Europe') {
        data = data.filter(d => d.continent === 'Europe');
    } else if (area === 'South America') {
        data = data.filter(d => d.continent === 'South America');
    } else if (area === 'Africa') {
        data = data.filter(d => d.continent === 'Africa');
    } else if (area === 'North America') {
        data = data.filter(d => d.continent === 'North America');
    } else {
        data = data;
    }

    data.forEach(d => {
        d.continent = d.continent;
        d.country = d.country;

        d.camp_1 = d.camp_1;

        d.camp_2 = d.camp_2;

        d.camp_3 = d.camp_3;

        // determine latest date for x-axis
        for (let i = 1; i <= 3; i++) {
            const date = `date_${i}`;

            if (d[date] !== '') {
                if (dates.indexOf(d[date]) === -1) {
                    dates.push(d[date]);
                }
            }
        }

        d.date_1 = parseTime(d.date_1);
        d.date_2 = parseTime(d.date_2);
        d.date_3 = parseTime(d.date_3);

        if (d.date_3) {
            duration = d.date_3 - d.date_1;
        } else {
            duration = d.date_2 - d.date_1;
        }

        d.duration = duration;
    });

    // sort MP list according to duration
    data.sort((a, b) => {
        return a.duration - b.duration;
    })

    // latestDate = d3.max(parsedDates);
    // console.log(latestDate);

    let jumpList = data.map(d => d.country);

    yScale.domain(jumpList);

    g.select('.y-axis')
        .transition()
        .duration(transTime)
        .call(d3.axisLeft(yScale));

    // change lines position
    let timeline = g.selectAll('.timeline').data(data);

    timeline.exit().remove();

    timeline.enter()
        .append('line');

    timeline.transition()
        .attr('x1', (d) => xScale(d.date_1))
        .attr('y1', (d) => yScale(d.country))
        .attr('x2', (d) => {
            if (d.date_3) {
                return xScale(d.date_3);
            } else if (d.date_2) {
                return xScale(d.date_2);
            }
        })
        .attr('y2', (d) => yScale(d.country))
        .attr('stroke', 'grey');

    let newCircles = g.selectAll('.time-circle').data(data);

    newCircles.exit().remove();

    newCircles.enter()
    .append('circle')

    // change circles position
    for (let i = 1; i <= 3; i++) {
        const date = `date_${i}`;
        const camp = `camp_${i}`;

        newCircles.transition()
            .duration(transTime)
            .attr('cx', d => {
                return xScale(d[date])
            })
            .attr('cy', d => yScale(d.country))
            .attr('r', '3')
            .style('fill', d => {
                return campColor(d[camp])
            });
    }
})
}

const campColor = (camp) => {
if (camp === 'AAA') {
return 'rgba(237,28,36,1)'
} else if (camp === 'BBB') {
return 'rgba(0,0,128,1)'
} else if (camp === 'CCC') {
return 'rgba(0,144,0,1)'
} else if (camp === 'DDD') {
return 'rgba(254,223,0,1)'
} else if (camp === 'EEE') {
return 'rgba(26,26,26,1)'
} else if (camp === 'FFF') {
return 'rgba(237,28,36,0.67)'
} else if (camp === 'GGG') {
return 'rgba(237,28,36,0.33)'
} else {
return 'rgba(255,165,0,1)';
}
}

我知道由于功能太相似而无法加载和更新同一张图表,这看起来很愚蠢,但这是我到目前为止所知道的。

因此,除了向我展示如何正确地用圆圈更新图表外,我还需要有关如何更好地重组代码的帮助。

我先谢谢你。

0 个答案:

没有答案