Two different y scales or two scales with different values

Now trying this:

var x = d3.time.scale().range([0, width]);
var y0 = d3.scale.linear().range([height, 0]);
var y1 = d3.scale.linear().range([height, 0]);

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

var yAxisLeft = d3.svg.axis().scale(y0)
    .orient("left").ticks(5);

var yAxisRight = d3.svg.axis().scale(y1)
    .orient("right").ticks(5); 

And my code:

        const min = 0;
        const max = 200;
        const min2 = 100;
        const max2 = 500;
        // Step 4
        let xScale = d3.scaleTime().domain([Jan1 , new Date]).range([0, width]).nice(),
            yScale = d3.scaleLinear().domain([min, max+10]).range([height, 0]).nice(),
            zScale = d3.scaleLinear().domain([min2, max2+10]).range([height, 0]).nice(),
            newX = xScale,
            newY = yScale,
            newZ = zScale;

        var yAxisLeft = d3.svg.axis().scale(yScale)
            .orient("left").ticks(10);

        var yAxisRight = d3.svg.axis().scale(zScale)
            .orient("right").ticks(10); 

already says: Uncaught TypeError: d3.svg.axis is not a function
at the line var yAxisLeft = d3.svg.axis().scale(yScale)

Wrong D3 version or problem with domain? But the book uses it similarly, scaleLinear, domain, range, nice.
At the end axisRight and axisLeft.

Later this has to be adapted and the right one has to be added.

        var axLft = g.append("g")
         .attr('id', 'y-axis')
         .call(d3.axisLeft(yScale));

my guess is

        var axLft = g.append("g")
         .attr('id', 'y-axis')
         .call(d3.axisLeft(yAxisLeft));

        var axRgt = g.append("g")
         .attr('id', 'y-axis')
         .call(d3.axisLeft(yAxisRight));

Seems to be complicated since it affects the data, zooming and percentages.

Wrong D3 version. v4 now uses d3.axis[Direction] (so axisTop, axisLeft, etc).

Well if it’s going on the right, you wouldnt call d3.axisLeft

It’s just another scale to be rescaled, and another axis to be redrawn…

This give no error message so is it correct?

        var yAxisLeft = d3.axisLeft(yScale);
    //                .orient("left").ticks(10);

        var yAxisRight = d3.axisRight(zScale);
    //                .orient("right").ticks(10); 

Now it has to be displayed.

To quote a good many threads and people on this forum… “what happened when you tried it?” :slight_smile:

This:

The initial pic is correct except for the extra blue line. Zooming works not yet for the new scale and of course the tooltip is shown at the wrong place, I read about that. Percentages are also not included for the new axis. is that just copy paste?

The blue line is the one shown first, if I rest Zoom the new one disappears and moves to the thin line.

The new axis should be in the same colour as the data so you know which it belongs to.
It is now exactly next to the original one. It could also be at the right side of the screen.

Edit: Modified some code to the percentages seem to work. The zooming function has to be expanded to include newZ.

            scatter
//            svg.append('g')
//            .select('#points')
            .selectAll("dot")
            .data(dataset3)
            .enter()
            .append("circle")
            .attr("cx", function (d) { return xScale(d[0]); } )
            .attr("cy", function (d) { return zScale(d[1]); } )

You define your Blue circles to be measured by the zScale.

    var line = d3.line()
            .x(function(d) { return xScale(d[0]); }) 
            .y(function(d) { return yScale(d[1]); })
            .curve(d3.curveMonotoneX)

You define line in terms of the yScale.

     scatterline
            .append("path")
            .datum(dataset3)
            .attr("id", "pointline")
            .attr("class", "line") 
            .attr("transform", "translate(" + 100 + "," + 100 + ")")
            .attr("d", line)
            .style("fill", "none")
            .style("stroke", "#0000FF")
            .style("stroke-width", "2")

You draw your blue line in terms of this y-scale measured line. So it wont line up with the circles because they’re using different scales for their y coordinates.

                    d3.selectAll('circle')
                    .attr("cx", (d) => { return newX(d[0]); })
                    .attr("cy", (d) => { return newY(d[1]); });

When you zoom, you tell it to scale ALL the circles to the y scale, not the z scale, causing it to jump.

So I have to double every code with a different id or class, like pointlineZ? Cause I cannot use only y or only z for all.

you’d need a variable for every combination of scales you use, yes.

Got this now:

Do I need a cz or z.function? Cause right now both y call y and z. Or an if case?

almost there. Two problems seem to exist at the moment:

  1. Your zoom is moving #pointline twice. The first one needs to move the red and green lines, and the second one needs to move the blue line only.
  2. Your zoom is moving all the circles to the newX/newY. The zoom should move blue circles to newX/newZ instead.

Yes, this code:

                d3.selectAll('circle')
                .attr("cx", (d) => { return newX(d[0]); })
                .attr("cy", (d) => { return newY(d[1]); });

Easiest would be to add another cz attribute.
Or there needs to be an if:

If scale = Z then use the code with newZ else newY, something like that?

Same with the pointline. So there could be one if and then both code blocks, correct?

                IF(){
                    d3.selectAll('circle')
                .attr("cx", (d) => { return newX(d[0]); })
                .attr("cy", (d) => { return newZ(d[1]); });


                d3.selectAll('#pointline')
                .attr("d",
                    d3.line()
                    .x(function(d) { return newX(d[0]); }) 
                    .y(function(d) { return newZ(d[1]); }) 
                    .curve(d3.curveMonotoneX)
                );

                }


                ELSE {
                    d3.selectAll('circle')
                .attr("cx", (d) => { return newX(d[0]); })
                .attr("cy", (d) => { return newY(d[1]); });
            
                d3.selectAll('#pointline')
                .attr("d",
                    d3.line()
                    .x(function(d) { return newX(d[0]); }) 
                    .y(function(d) { return newY(d[1]); }) 
                    .curve(d3.curveMonotoneX)
                );
                }

But I get two red warnings at the IF and ELSE in VS Studio Code.

cz is meaningless to a 2-dimensional circle that’s defined by cx,cy.

You’ll need to separate your circles somehow (hint: how about giving the blue circles a class?), and operate independently on the red/green circles and the blue circles, similarly to how you’re operating on your blue line separately from the red/green ones.

you… could do something like this, but your function for cy would have to get more indepth (find the element that is being referenced, find its color, operate accordingly)… or yuou could duplicate the code and split red/green and blue operations that way.

        scatter
        .selectAll("dot")
        .data(dataset3)
        .enter()
        .append("circle")
        .attr("cx", function (d) { return xScale(d[0]); } )
        .attr("cy", function (d) { return zScale(d[1]); } )
        .attr("r", 5)
        .attr("transform", "translate(" + 100 + "," + 100 + ")")
        .style("fill", "#0000FF")
        .on("mouseover",movetooltip)
        .on("mouseout", mouseout);

add:

.attr('class','blau');

And then if class = blau do this else do that.

The line code already has class “line”.

        scatterline
        .append("path")
        .datum(dataset3)
        .attr("id", "pointline")
        .attr("class", "line") 
        .attr("transform", "translate(" + 100 + "," + 100 + ")")
        .attr("d", linez)
        .style("fill", "none")
        .style("stroke", "#0000FF")
        .style("stroke-width", "2")

So can I use two classes? Line and Line & Blau?

close. select will take any valid CSS selector; so .blau will select anything with the “blau” class.

yeah, if you put “line blau” in the class attribute, then it would have both the line and blau classes. Be careful though - you dont want to grab your line with your circle-moving code!

1 Like

so some selectors that might be helpful to you when using select and selectAll

circle : “Any <circle> element.”
path.blau : “Any <path> element with class blau.”
circle.blau : “Any <circle> element with class blau.”
.blau : “Any element with the class blau.”
#pointline : “Any element with the ID pointline” (You should only have 1 element with a given id… but d3 works with bad HTML.)
.line.blau : “Any element with BOTH classes line and blau

Now it doesn’t zoom at all, used classed() and thought !#blau would select everything that is not blau.

No, you cant negate CSS selectors that way; the negation for blau would be :not(.blau) (IE: circle:not(.blau) if you gave your blue circles the class blau…)

CSS selectors:
Tag Starts with nothing: name : selects tags called <name>
Class Starts with a period: .blau : selects things with the class blau.
ID Starts with a hash: #pointline : selects things with the id pointline.

So in this case:

                d3.selectAll('circle')
                .classed('#blau')
                .attr("cx", (d) => { return newX(d[0]); })
                .attr("cy", (d) => { return newZ(d[1]); });

                d3.selectAll('circle')
                .not(classed('#blau'))
                .attr("cx", (d) => { return newX(d[0]); })
                .attr("cy", (d) => { return newY(d[1]); });

Is all the rest correct?

=>

                d3.selectAll('circle:not(.blau)')
                .attr("cx", (d) => { return newX(d[0]); })
                .attr("cy", (d) => { return newY(d[1]); });