Диаграмма силы d3 каждый раз рисует по-разному - PullRequest
0 голосов
/ 02 октября 2019

Я использую силу d3, чтобы создать диаграмму теплого пчеловодства, которая разделяется на групповую категорию с помощью переключателя. У меня все работает, кроме того, когда я разгруппирую, сгруппирую, а затем разгруппирую, позиция y, похоже, становится все меньше и меньше по дальности.

пример первой разгруппировки: enter image description here

при группировке: enter image description here

Вторая разгруппировка: enter image description here

Любая помощь в правильном направлении очень ценится.

Вот мой код для диаграммы:

         Dimensions for chart
         */
        this.width = 900;
        this.height = 750;
        this.margin = {x: 50, y:100};

        this.data = data;

        svg.attr('height', (this.height +  this.margin.y)).attr('width', this.width + (this.margin.x * 2));
        svg.append('g').attr('id', 'circle-wrap').attr('transform', `translate(${this.margin.x},${this.margin.y})`);


        let xAxis = svg.append('g').attr('id', 'x-axis');

        this.updateBubbleChart(d3.select('#separate').node().checked, svg, data);

    }

    /**
     * Updates the bubble chart
     * @param {Boolean} separate 
     * @param {*} svg 
     * @param {*} data 
     */
    updateBubbleChart(separate, svg, data){

        //GETTING OVERALL MIN AND MAX FOR SCALES//
        let difMax = d3.max(data.map(d=> d.position));
        let difMin = d3.min(data.map(d=> d.position));

        //SO WE CAN SEE THE DIFFERENCES IN POLARITY ON THE SAME SCALE, 
        //TAKE THE MIN AND MAX AND GET THE MAX VALUE FOR THAT. 
        //USE POS AND NEG OF THAT VALUE FOR DOMAIN.
        let overallMax = d3.max([Math.abs(difMin), difMax]);

        //USING D3.GROUPS TO GROUP DATA BY CATEGORY
        let groupData = d3.groups(data, d=> d.category);
        //SORT BY LENGTH OF GROUP?

        let groupKeys = groupData.map((g, i)=> {
            return {key:g[0], pos: i};
        });

        //SCALE FOR X POSITION//
        let xScale = d3.scaleLinear().domain([(0-overallMax), overallMax]);
        xScale.range([0, this.width]);

        //SCALE FOR Y POSITION//
        let yScale = d3.scaleLinear().domain([0, groupKeys.length - 1]).range([0, this.height - this.margin.y])

        //SCALES FOR COLOR//
        let colorScale = d3.scaleOrdinal().domain(groupKeys.map(k=> k.key)).range(d3.schemeSet2);

        //SCALE FOR CIRCLE SIZE//
        let circleScale = d3.scaleLinear().domain([d3.min(data.map(d=> +d.total)), d3.max(data.map(d=> +d.total))])
        .range([3, 12]);

        //SIMULATION PART
        let simulation = d3.forceSimulation().nodes(data)
        .force('x', d3.forceX(d => xScale(d.position)).strength(1))
        .force('y', d3.forceY().strength(1))
        .force('collision', d3.forceCollide().radius( d => circleScale(d.total)))
        .on('tick',ticked);

        let xAxis = d3.axisTop().scale(xScale);

        let xAxisG = d3.select('#x-axis').call(xAxis).attr('transform', `translate(${this.margin.x}, ${this.margin.y/2})`)

        let circleGroup = d3.select('#circle-wrap');
        //BINDING CIRCLES TO THE DATA
        let circles = circleGroup.selectAll('circle').data(data).join('circle');
        circles.attr('r', (d)=> circleScale(+d.total))//.attr('cx', (d) => posScale(d.position)).attr('cy', 50);
        .attr("cx", d=> xScale(+d.position))
        circles.attr('fill', (d)=> colorScale(d.category))
        .attr('stroke', 'black').attr('stroke-width', 0.3);
        circles.style('opacity', '0.9');

        //TOOLTIP FOR CIRCLES ON HOVER///
            //Add the tooltip labels on mouseover
            circles.on('mouseover', (d, i, n)=> {
                //SHOW DATA IN TOOLTIP
                d3.select('#tooltip').transition()
                    .duration(200)
                    .style("opacity", .9);
                    d3.select('#tooltip').html(tooltipRender(d) + "<br/>")
                    .style("left", (d3.event.pageX + 5) + "px")
                    .style("top", (d3.event.pageY - 28) + "px");
                //HIGHLIGHT CIRCLE
                d3.select(n[i]).attr('stroke', 'black').attr('stroke-width', 2);
            });

            circles.on("mouseout", function(d, i, n) {
                d3.select('#tooltip').transition()
                    .duration(500)
                    .style("opacity", 0);
                //REMOVE CIRCLE HIGHLIGHT
                d3.select(n[i]).attr('stroke', 'black').attr('stroke-width', 0.3);
            });

        // Apply these forces to the nodes and update their positions.
        // Once the force algorithm is happy with positions ('alpha' value is low enough), simulations will stop.
        function ticked(){
            circles.attr("cy", d=> d.y).attr('cx', d=> d.x);
            if(separate === true){
                simulation.force('y', d3.forceY().y( d => {
                    let move = groupKeys.filter(g=> g.key === d.category)[0].pos;
                    return yScale(move);
                }));
            }
        }

...