简体   繁体   中英

User-selected marker in time series data in Java

My code plots 5000 points of time series data in a panel that is 581 pixels wide by default, but this width changes when the user resizes the window. My code also plots several rectangular markers that each identify a local maximum/peak in this same space.

I need to enable the user to right click on any of the rectangular-peak-markers so that the user can manually delete any false peak. The problem is that my code is reporting different x-coordinates than expected when the user right-clicks on a peak-marker. I suspect that the reason may have to do with rounding error in converting from 581 x-pixels back to 5000 data indices. But I am not certain of the reason.

Can anyone suggest a solution that enables my users to manually select one of the above-described peak markers by right-clicking on it?

I am enclosing relevant sections of the code below. My actual code is very, very long, and too complicated to post. But the relevant portions below should be enough for someone to see the logic of my approach, and to then suggest a more effective approach.

The code that declares the class in question is:

class SineDraw extends JPanel implements MouseMotionListener, MouseListener {
// lots of code, including the two segments excerpted below
}

This segment of code overloads the paintComponent of the JPanel so that my data is plotted:

// declare some variables
ArrayList<Double> PeakList = new ArrayList<Double>()  // this ArrayList is populated by an extraneous process
visiblePoints = 5000
hstep = getWidth()/visiblePoints //=581/5000 by default, but will change when user resizes window
int numPeaks = PeakList.size();

// scale (y-coordinate) data relative to height of panel
pts = new double[visiblePoints]
for (int i = 0; i < pts.length-1; i++){pts[i]=//data vertical scaled to fill panel;}

// plot the 5000 time-series-data-points within the 581 pixels in x-axis
for (int i = 1; i < visiblePoints; i++) {
    int x1 = (int) ((i - 1) * hstep);
    int x2 = (int) (i * hstep);
    int y1 = (int)pts[i - 1];
    int y2 = (int)pts[i];
    g2.drawLine(x1, y1, x2, y2);
}

// plot a rectangle for each of the local peaks
for(int m=0;m<=(numPeaks-1);m++){
    if(i==(int)(PeakList.get(m)){
        int currentVal = (int)pts[(int)(PeakList.get(m)];
        g2.drawRect((int)(PeakList.get(m), currentVal, 6, 6);
    }
}

This section of code is for handling the right-clicking of the mouse:

public void mousePressed(MouseEvent e){
    // check to see if right mouse button was clicked
    boolean jones = (e.getModifiers()&InputEvent.BUTTON3_MASK)==InputEvent.BUTTON3_MASK;
    if(jones==true){
        // test the value returned as x-coordinate when user right-clicks (code always underestimates x-coordinate of local peaks by this test)
        double ReverseHstep = visiblePoints/getWidth();
        int getX_ConvertedTo_i = (int) (e.getX()*ReverseHstep);
        System.out.println("getX_ConvertedTo_i is:  "+getX_ConvertedTo_i );

        // check to see if peaklist contains a value within the x-coordinates of the user-selected-rectangle
        if(PeakList.contains((double)(e.getX()-3))
                ||PeakList.contains((double)(e.getX()-2))
                ||PeakList.contains((double)(e.getX()-1))
                ||PeakList.contains((double)(e.getX()))
                ||PeakList.contains((double)(e.getX()+1))
                ||PeakList.contains((double)(e.getX()+2))
                ||PeakList.contains((double)(e.getX()+3))
                ){
                // handling code will go here, but for now it is a print test that never succeeds because x-coordinate is always underestimated
                System.out.println("You just selected a peak!");
        }
        }
    repaint();
    }

I suggest you create objects (in this case Rectangles ) for each thing you want to be clickable. Here is an over-simplified example of how you can make something you draw clickable. The key thing to take away from this is the mouseClicked method which will display a dialog only if the mouse clicked within the rectangle.

One tricky point is that I wasn't able to figure out how to make the rectangle filled in with color without drawing another rectangle over it. I'll leave that one for you ;-)

public class Canvas extends JPanel implements MouseListener{
    private Rectangle rect = new Rectangle(100,100);

    public Canvas(){
        this.addMouseListener(this);
        rect.setSize(100, 100);
    }

    @Override
    public void paintComponent(Graphics g){
        g.setClip(rect);
        g.setColor(Color.RED);
        g.fillRect(0, 0, 100, 100);
    }

    @Override
    public void mouseClicked(MouseEvent e){
        if(rect.contains(e.getPoint())){
            JOptionPane.showConfirmDialog(this, "Click!");
        }
    }

    // The rest of the MouseListener methods have been cut out

    public static void main(String[] a){
        JFrame frame = new JFrame("Canvas Thingy");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setBounds(0, 0, 300, 300);
        frame.add(new Canvas());
        frame.setVisible(true);
    }
}

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