✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
🐇 Announcing Dash VTK for 3d simulation graphics. Check out the March webinar.

[Resolved] Changing dragmode using Plotly.relayout

I’ve been trying to create a plot where the user can choose to zoom, select an area of the plot of which can make a selection, or some combination of zooming and selecting. I’m encountering a few problems

  1. There are problems appearing in a few places. Let me try to list them:
    • If you try zooming and then pressing addSelection then you get a plot of the zoomed image
    • Now double click the plot to zoom out. You’ll see the original plot
  2. When you zoom out of the original plot, the name changes. I’m not sure why
    • If you zoom and then Begin Selection, you can select the desired points on the plot and export that to a new graph
    • When you try to zoom back out on the original plot, you don’t get the original plot.
  3. If you just use Begin Selection without zooming, then you get the section of the plot you want
    • However, the original plot’s name changes
  4. Zoom in, Press Begin Selection, select some content, press End Selection, and attempt to zoom out. Instead of seeing the whole plot, you zoom into the previously selected content.

I’m willing to attribute these bugs to not having a thorough understanding of how relayout works (or another Plotly.js feature). However, if anyone could offer an explanation of why these bugs appear or a suggestion on where to go, I would be immensly grateful.

Here is some reproducible code:

<body>
    <div>

        <button onclick="downloadMainPlot()">Download</button>
        <button onclick="beginSelecting()">Begin Selecting</button>
        <button onclick="endSelecting()">End Selecting</button>
        <button onclick="addSelection()">Add Selection</button>
    </div>
    <div id="graph">
        <!-- Plotly chart will be drawn inside this DIV -->
    </div>
    <div id="selectedPlots">
        <!-- All confirmed selections will be drawn inside this DIV -->
    </div>
    <script>
        var graphDiv = document.getElementById('graph');
        var MAX_POINTS = 100;

        const layout = {
            dragmode: 'zoom',
            title: 'Random Increase',
            xaxis: {
            },
            yaxis: {
            },
            legend: true
        };
        var x = [];
        var y = [];
        for (var i = 0; i < MAX_POINTS; i++) {
            x.push(i);
            y.push(i / (Math.cos(i) + 2) + Math.sin(i));
        }

        const trace1 = {
            name: 1,
            x: x,
            y: x,
            type: 'scatter',
            mode: 'lines+markers',
            marker: {
                opacity: 0,
            },
            line: {
                color: 'rgb(82, 64, 219)',
            }
        };
        const trace2 = {
            name: 2,
            x: x,
            y: y,
            type: 'scatter',
            mode: 'lines+markers',
            marker: {
                opacity: 0,
            },
            line: {
                color: 'rgb(219, 64, 82)',
            }
        };
        var data = [trace1, trace2];
        Plotly.newPlot(graphDiv, data, layout, { displayModeBar: false });

        var selectCount = 0;
        var traceNew1 = trace1,
            traceNew2 = trace2;

        graphDiv.on('plotly_selected', function (eventData) {
            traceNew1.x = [];
            traceNew1.y = [];
            traceNew2.x = [];
            traceNew2.y = [];

            if (eventData) {
                if (eventData.points) {
                    eventData.points.forEach(function (pt) {
                        if (pt.data.name == 1) {
                            traceNew1.y.push(pt.y);
                            traceNew1.x.push(pt.x);
                        } else if (pt.data.name == 2) {
                            traceNew2.y.push(pt.y);
                            traceNew2.x.push(pt.x);
                        }
                    });
                }
            }
        });

        function addSelection() {
            var newSelect = document.createElement('DIV');

            var newID = 'plot' + selectCount.toString();
            newSelect.setAttribute("id", newID);
            console.log('new DIV', newSelect);
            // Display new Plot before old
            if (!selectCount) {
                document.getElementById('selectedPlots').appendChild(newSelect);
            } else {
                document.getElementById('selectedPlots').insertBefore(newSelect, document.getElementById('selectedPlots').firstChild)
            }

            var update = {
                title: "New Plot #" + selectCount.toString(),
                dragmode: 'zoom',
            };

            Plotly.newPlot(
                document.getElementById(newID),
                [traceNew1, traceNew2],
                layout,
                {
                    displayModeBar: false
                }
            );
            Plotly.relayout(document.getElementById(newID), update);

            newSelect = null;
            selectCount++;
        }

        function beginSelecting() {
            console.log('beginSelecting Activated');
            Plotly.relayout(graphDiv, { dragmode: 'select' });
        }
        function endSelecting() {
            console.log('endSelecting Activated');
            Plotly.relayout(graphDiv, { dragmode: 'zoom' });
        }

        function downloadMainPlot() {
            Plotly.downloadImage(graphDiv, { format: 'png', width: 800, height: 600, filename: 'newplot' });
        }

        graphDiv.on('plotly_relayout', function (ed) {
            console.log('Plotly_relayout ', ed);
        });
    </script>
</body>

Here’s a codePen of the above code snippet: https://codepen.io/etpinard/pen/xWYmVo?editors=0010

Af first glance, your issues seem more integration based rather than pure plotly.js problems. So, I won’t spend time to look at this in details. These are free forums afterall.

I hope someone here can help you out.

Well, it’s good to know that the problem’s on my side! I’ll keep at it and hopefully find what I’m missing. I’ll post a result for anyone trying something similar.

In the meantime, I would be grateful if anybody would be willing to share their thoughts.

I’m not sure what’s different between this and the code I submitted before, but it works without any of those issues. If anyone could offer an explanation of why I was encountering those I’d greatly appreciate it.

If you’re trying to do the same thing, help yourself to my changes in the code:

function beginSelecting() {
            document.getElementById('begin').setAttribute("style", "display:none;");
            document.getElementById('end').setAttribute("style", "display:'';");

            Plotly.relayout(graphDiv, { dragmode: 'select' });
        }
        function endSelecting() {
            document.getElementById('end').setAttribute("style", "display:none;");
            document.getElementById('begin').setAttribute("style", "display:'';");

            Plotly.relayout(graphDiv, { dragmode: 'zoom' });
        }
        var selectCount = 0;
        function addSelection() {
            // Create new DiV
            var newPlot,
                plotID = 'plot_' + selectCount,
                plotDiv = document.getElementById('selectedPlots');
            newPlot = document.createElement('DIV');
            newPlot.setAttribute('id', plotID);
            if (!selectCount) {
                plotDiv.appendChild(newPlot);
            } else {
                plotDiv.insertBefore(newPlot, plotDiv.childNodes[0])
            }

            // Prepare Plot
            var newLayout = {
                dragmode: 'zoom',
                title: 'New Plot #' + selectCount,
                xaxis: {
                },
                yaxis: {
                },
                legend: true
            },
                newData = [],
                separatedData = {};

            selectData.forEach(function (datum) {
                if (!(datum.data.name in separatedData)) {
                    separatedData[datum.data.name] = {
                        x: [],
                        y: [],
                    }
                }
                separatedData[datum.data.name].x.push(datum.x);
                separatedData[datum.data.name].y.push(datum.y);
            });
;
            for (var key in separatedData) {
                newData.push({
                    name: 'new_' + selectCount + '_' + key,
                    x: separatedData[key].x,
                    y: separatedData[key].y,
                    type: 'scatter',
                    mode: 'lines+markers',
                    marker: {
                        opacity: 0,
                    },
                    line: {
                    }
                });
            };

            if (newData.length < 1) {
                newData = data;
                newLayout.title = 'Original';
            }

            Plotly.newPlot(
                newPlot,
                newData,
                newLayout,
                { displayModeBar: false }
            );
            selectCount++;
            selectData = [];
        }