Moving and resizing an IntervalMarker

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
JimLahey
Posts: 6
Joined: Thu Jan 22, 2015 6:20 pm
antibot: No, of course not.

Moving and resizing an IntervalMarker

Post by JimLahey » Thu Jan 22, 2015 6:48 pm

Hi all,

As far as I can tell there is no way to move or resize an IntervalMarker once it has been added to an XYPlot. Is there something I am missing and this behaviour IS possible? The only solution I can see is to write these myself but I am unsure of MouseChartEvents and how to detect when the mouse button has been depressed and being dragged.

Any help with this would be much appreciated.

Thanks

JimLahey
Posts: 6
Joined: Thu Jan 22, 2015 6:20 pm
antibot: No, of course not.

Re: Moving and resizing an IntervalMarker

Post by JimLahey » Fri Jan 23, 2015 11:42 am

Hi,

I have begun work on extending the functionality of IntervalMarker to make it draggable and resizeable but am having some trouble...

As the class itself inherits so low down the Java hierarchy (Object), it does not have the ability to add MouseMotionListener which I need to write this functionality. I have tried to replicate how JPanel implements the addition of a MouseMotionListener but when I do it does not register that I have clicked the IntervalMarker. In addition to that, when I do click and drag... the JFreeChart automatically starts zooming ( FYI: I would ideally like to suppress the zooming behaviour on the left-click and just have it do it on right-clicking ).

Can anyone offer any suggestions with this?

Thanks

paradoxoff
Posts: 1634
Joined: Sat Feb 17, 2007 1:51 pm

Re: Moving and resizing an IntervalMarker

Post by paradoxoff » Fri Jan 23, 2015 1:38 pm

Hi Jim,
Several years ago, I have tried to do something similar (see here), but finally gave up because getting it to work as I wanted appeared to require too many changes to the API.
A few weeks ago, I have started to look at draggable markers again. As a first step, instead of changeing the way JFreeChart is handling markers, I generated a set of Annotations that serve the same purpose as markers. These classes (XYDomainValueAnnotation, XYDomainIntervalAnnotation, XYRangeValueAnnotation, XYRangeIntervalAnnotation, plus a few others for CategoryPlots) are nearly finished. I could upload them to the patch manager on sourceforge (if this is not considered an abuse of the storage space). It turned out that everything than can be done with markers can also be done with an annotation (I even believe that markers are not required at all, unless you need them for some 3D plots, but it should be possible to integrate the logic to generate a 3D effect in the annotations also). Annotations already support the generation of XYAnnotationEntities. In order to make these "XYDomainIntervalAnnotations" and "XYRangeIntervalAnnotations" draggable and resizeable, you could try the following approach:
- Overwrite the addEntity-Method of AbstractXYAnnotation and add a new type of "AnnotationEntity" to the entity collection that has a refence to the annotation itself. The available XYAnnotationEntity does not contain a reference to the annotation by which it was generated.
- Add a ChartMouseListener to the ChartPanel containing the plot with the new annotation type.
- Since a ChartMouseListener does not listening for drag events (that would probably collide with the mouse dragging logic directly implemented in the ChartPanel), you could implement a ChartMouseListener-logic based on mouse clicks and mouse movements. The ChartMouseListener would need to store the last selected annotation as a candidate for size or position change, and also the coordinates of the mouse events.
- If the mouse is clicked inside the area of an annotation entity, mark the referenced annotation in some way as a possible candidate to a move or resize. Store the coordinates of the mouse event. Based on the exact coordinates of the mouse event, you should be able to distinguish whether the click occured on the main area of a line or interval annotation or at the borders of an interval annotation.
- If the mouse if moved, check whether an annotation has been marked as described above. Based on the coordinates of the last mouse event (stored above) and the coordinates of the current mouse event, change the start and/or end value of the stored annotation.

HTH
Peter

JimLahey
Posts: 6
Joined: Thu Jan 22, 2015 6:20 pm
antibot: No, of course not.

Re: Moving and resizing an IntervalMarker

Post by JimLahey » Mon Jan 26, 2015 10:33 am

Hi Peter,

It would be really cool if you could upload these classes that you have been working on. The approach that I had begun to take was to use Java's own MouseMotionListener which does handle dragging and such. The problem I had with this approach was that IntervalMarker extends Marker and Marker extends Object. This meant that I did not have access to adding MouseMotionListener to the IntervalMarker. I then tried to add my own function to a derived IntervalMarker to add MouseMotionListener but have not been having much luck with that. Which Java class do your annotation classes inherit? Would I be able to do something like what I had been attempting?

Thanks

JimLahey
Posts: 6
Joined: Thu Jan 22, 2015 6:20 pm
antibot: No, of course not.

Re: Moving and resizing an IntervalMarker

Post by JimLahey » Mon Jan 26, 2015 10:45 am

I see what you are saying about having the listener attached to the actual ChartPanel and work out if we are inside the annotation area. The problem with this I have found is that ChartPanel seems to be in actual screen pixels. It also takes the area at the start and end of the Chart that is added as it is the whole area of the ChartPanel. I do not know how to subtract this, the remaining value would be the JFreeChart area in real pixels whereas I want to work with pixels in terms of the way they are plotted in XYPlot. They are not the same thing obviously.
Last edited by JimLahey on Mon Jan 26, 2015 1:31 pm, edited 2 times in total.

JimLahey
Posts: 6
Joined: Thu Jan 22, 2015 6:20 pm
antibot: No, of course not.

Re: Moving and resizing an IntervalMarker

Post by JimLahey » Mon Jan 26, 2015 11:57 am

Hi Peter,

Could you also explain the part below a little further? A pseudo-code example would be extremely useful.

" Overwrite the addEntity-Method of AbstractXYAnnotation and add a new type of "AnnotationEntity" to the entity collection that has a refence to the annotation itself. The available XYAnnotationEntity does not contain a reference to the annotation by which it was generated. "

Thanks

paradoxoff
Posts: 1634
Joined: Sat Feb 17, 2007 1:51 pm

Re: Moving and resizing an IntervalMarker

Post by paradoxoff » Mon Jan 26, 2015 10:34 pm

The patch with the source files is uploaded. The classes should be regarded as incomplete. Proper implementation of equals, hashCode and for serialization might be missing.
A few comments on your other questions/comments:
Reagrding the addition of mouse listenres directly to markers/annootations: writing a Marker/Annotation tat allows to add mouse listeners will of course only work if the marker/annotation gets informed when the mouse is over its area! I can#t image an easy way to achieve that. Descendants of java.awt.Component (such as a ChartPanel) have this capability built in, but since markers and annotations have completely different ancestors, the story is much more complicated.
Regarding the difference between pixels in a ChartPanel and in an XYPlot: to do this "coordinate conversion", you can use the methode translateScreenToJava2D(java.awt.Point screenPoint) of the ChartPanel class.
Regarding
" Overwrite the addEntity-Method of AbstractXYAnnotation and add a new type of "AnnotationEntity" to the entity collection that has a refence to the annotation itself. The available XYAnnotationEntity does not contain a reference to the annotation by which it was generated. ": JFreeChart has a built-in mechanism to store the regions that are occupied by some objects during the rendering of the chart. The "regions" (or entities) are primarily shapes (with some additional informations). Please have a look at the classes in the package org.jfree.chart.entity to see what is available. These entities are also recognized during several mouse actions on a ChartPanel and can for example be retrieved from a ChartMouseEvent. Most entities contain a reference to the "object" by which they were drawn. For example, an XYItemEntity has a reference to an XYDataset, a series index and an item index and thus allows to retrieve the x, y values in the dataset for the given item. An AxisEntity has a reference to an Axis. In a suitable ChartMouseListener implementation, it is thus possible to get the Axis and do some stuff with it, such as showing a dioalog that allows to edit the axis' properties (I have implemented something like that already). An XYAnnotationEntity does not contain a reference to an XYAnnotation. The XYAnnotationEntity can be used to obtain a tool tip text and show it, but does not allow to access the XYAnnotation itself and change their properties. But getting a reference to the annotation is a prerequisite to change is properties.
[Edit: some classes are missing from the uploaded ZIP file. I will upload a complete archive later today.]
Here is a short demo that uses the new classes:

Code: Select all

public class SimpleXYPlot {

    public static void main(String[] args) {
        int count = 500;
        int series = 5;
        DefaultXYDataset dataset = new DefaultXYDataset();
        long start = System.currentTimeMillis();
        long increment = 1000*60*20; //20 min
        for(int s = 0; s < series; s++){
            double[][] data = new double[2][count];
            for(int i = 0; i < count; i++){
                data[0][i] = start + increment * i;
                data[1][i] = (s + 1)*i;
            }
            dataset.addSeries("Series " + s, data);
        }
        DateAxis xAxis = new DateAxis("x axis");
        NumberAxis yAxis = new NumberAxis("y axis");
        XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(false, true);
        renderer.setUseOutlinePaint(true);
        Shape shape = new Ellipse2D.Double(-4, -4, 8, 8);
        for(int i = 0; i < series; i++){
            renderer.setSeriesShape(i, shape);
            renderer.setSeriesOutlinePaint(i, Color.black);
        }
        XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer);
        plot.setInsets(new RectangleInsets(50, 50, 50, 50));
        XYRangeIntervalAnnotation ria = new XYRangeIntervalAnnotation(500, 750);
        ria.setLabel("RangeInterval");
        ria.setLabelTextAnchor(TextAnchor.CENTER_LEFT);
        ria.setLabelAnchor(RectangleAnchor.LEFT);
        plot.addAnnotation(ria);
        XYDomainIntervalAnnotation dia = new XYDomainIntervalAnnotation(System.currentTimeMillis()+172800000L,System.currentTimeMillis()+172800000L +86400000L );
        dia.setLabel("Interval\n\rMutiline\n\rZeile 3\n\rZeile 4");
        dia.setPaint(Color.RED);
        dia.setLabelTextAnchor(TextAnchor.TOP_CENTER);
        dia.setLabelAnchor(RectangleAnchor.CENTER);
        dia.setRotationAnchor(TextAnchor.TOP_CENTER);
        plot.addAnnotation(dia);
        XYDomainValueAnnotation dva = new XYDomainValueAnnotation();
        dva.setLabel("Test Value Annotation");
        //a.setLabelAngle(Math.PI/4.0);
        dva.setStroke(new BasicStroke(5.0f));
        dva.setPaint(Color.RED);
        dva.setValue(System.currentTimeMillis()+86400000L);
        dva.setLabelTextAnchor(TextAnchor.TOP_LEFT);
        dva.setLabelAnchor(RectangleAnchor.TOP_LEFT);
        dva.setRotationAnchor(TextAnchor.TOP_LEFT);
        plot.addAnnotation(dva);
        XYDomainValueAnnotation dva1 = new XYDomainValueAnnotation();
        dva1.setLabel("Second Test");
        //a.setLabelAngle(Math.PI/4.0);
        dva1.setStroke(new BasicStroke(5.0f));
        dva1.setPaint(Color.RED);
        dva1.setValue(System.currentTimeMillis()+3*86400000L);
        dva1.setLabelTextAnchor(TextAnchor.TOP_LEFT);
        dva1.setLabelAnchor(RectangleAnchor.TOP_RIGHT);
        dva1.setRotationAnchor(TextAnchor.TOP_LEFT);
        plot.addAnnotation(dva1);
        XYRangeValueAnnotation rva = new XYRangeValueAnnotation();
        rva.setValue(1111);
        rva.setLabel("Anno Test");
        Font f = new Font("Tahoma",1,12);
        rva.setLabelFont(f);
        rva.setStroke(new BasicStroke(3.0f));
        rva.setPaint(Color.BLUE);
        rva.setLabelTextAnchor(TextAnchor.CENTER_LEFT);
        rva.setLabelAnchor(RectangleAnchor.LEFT);
        plot.addAnnotation(rva);
        ValueMarker vm = new ValueMarker(1211);
        vm.setLabel("Marker Test");
        vm.setStroke(new BasicStroke(3.0f));
        vm.setPaint(Color.BLUE);
        vm.setLabelTextAnchor(TextAnchor.CENTER_LEFT);
        vm.setLabelAnchor(RectangleAnchor.LEFT);
        vm.setLabelFont(f);
        plot.addRangeMarker(vm);
        BasicStroke lineStroke = new BasicStroke(1.0f);
        plot.setDomainMinorGridlinesVisible(true);
        plot.setDomainMinorGridlineStroke(lineStroke);
        plot.setDomainGridlineStroke(lineStroke);
        plot.setDomainMinorGridlinePaint(Color.gray);
        plot.setDomainGridlinePaint(Color.BLACK);
        JFreeChart chart = new JFreeChart(plot);
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new ChartPanel(chart));
        frame.pack();
        frame.setVisible(true);
    }
}
Unfortunately, I have no sample code on how to make those annotations interactively moveable, and I won´t be able to write something up at until end of this week, but maybe you can try how on your own and see how far you get.

HTH, Peter

Second edit: new ZIP file containing all missing classes has been uploaded.

JimLahey
Posts: 6
Joined: Thu Jan 22, 2015 6:20 pm
antibot: No, of course not.

Re: Moving and resizing an IntervalMarker

Post by JimLahey » Mon Feb 02, 2015 10:15 am

Hi Peter,

Thank you for all of your help but I have settled on a simpler solution for what I needed. I was on a bit of a tight schedule with it and had to make a decision. I would have loved to have spent some time on getting this working as it was the way I wanted to do it but I am sure you appreciate time constraints involved when trying to get things out the door :P

Regards

Locked