How to sort legend items along with its symbols

TeeChart for Java (NetBeans, Eclipse, Android Studio, etc)
Post Reply
Jonathan
Newbie
Newbie
Posts: 60
Joined: Tue Mar 11, 2008 12:00 am
Location: Austin

How to sort legend items along with its symbols

Post by Jonathan » Tue Mar 27, 2012 8:26 pm

Hi.

I want to know if it is possible to sort the legend items along with its symbols in TeeChart?

For example, when I sort the legend items in box chart, but the symbols do not get sorted along with its legend items. I think it is because the index from getItemText of legendResolver is something I can't change.

Right now, the legend items and symbols are added in the order of rendering chart series. Could you help me how to sort the legend items by alphabetically or numerically?

Yeray
Site Admin
Site Admin
Posts: 9613
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: How to sort legend items along with its symbols

Post by Yeray » Thu Mar 29, 2012 12:04 pm

Hi Jonathan,

I'm afraid it's not possible "as-is". However, you can calculate the order you wish and play with LegendResolver to modify the legend strings and the chartPainted event to draw the symbols over the old ones. For example:

Code: Select all

	private static void initializeChart() {
		tChart1.getAspect().setView3D(false);
		
		Random r = new Random();
		for (int i=0; i<4; i++) {
			new Line(tChart1.getChart());
			tChart1.getSeries(i).fillSampleValues(5);
			tChart1.getSeries(i).setTitle("Series" + String.valueOf(r.nextInt(100)));
		}
		
		final int[] seriesOrder;
		
		if (tChart1.getSeriesCount() > 1) {
			seriesOrder = new int[tChart1.getSeriesCount()];
			
			for (int i=0; i<tChart1.getSeriesCount(); i++)
				seriesOrder[i] = i;
			
			for (int i=0; i<seriesOrder.length; i++) {
				for (int j=i+1; j<seriesOrder.length; j++) {
					if (tChart1.getSeries(seriesOrder[j]).titleOrName().compareTo(tChart1.getSeries(seriesOrder[i]).titleOrName()) < 0) {
						int tmp = seriesOrder[i];
						seriesOrder[i] = seriesOrder[j];
						seriesOrder[j] = tmp;
					}
				}
			}
		}
		else {
			seriesOrder = new int[tChart1.getSeries(0).getCount()];
			
			for (int i=0; i<tChart1.getSeries(0).getCount(); i++)
				seriesOrder[i] = i;
			
			for (int i=0; i<seriesOrder.length; i++) {
				for (int j=i+1; j<seriesOrder.length; j++) {
					if (tChart1.getSeries(0).getYValues().getValue(seriesOrder[j]) < tChart1.getSeries(0).getYValues().getValue(seriesOrder[i])) {
						int tmp = seriesOrder[i];
						seriesOrder[i] = seriesOrder[j];
						seriesOrder[j] = tmp;
					}
				}
			}
		}
		
		tChart1.addChartPaintListener(new ChartPaintAdapter() {
			
			@Override
			public void chartPainted(ChartDrawEvent e) {
				if (tChart1.getSeriesCount() > 1) {
					for (int i=0; i<tChart1.getLegend().getLegendItems().size(); i++) {
						Rectangle rect = tChart1.getLegend().getLegendItems().getLegendItem(i).getSymbolRect();
						
						if ((tChart1.getSeries(seriesOrder[i]) instanceof Custom) && (((Custom)tChart1.getSeries(seriesOrder[i])).getPointer().getVisible())) {
							tChart1.getGraphics3D().getBrush().setColor(tChart1.getSeries(seriesOrder[i]).getColor());
							tChart1.getGraphics3D().rectangle(rect);
						}
						else {
							tChart1.getGraphics3D().getPen().setColor(tChart1.getSeries(seriesOrder[i]).getColor());
							tChart1.getGraphics3D().horizontalLine(rect.x, rect.getRight(), (rect.y + rect.getBottom()) / 2);
						}
					}
				}
				else {
					for (int i=0; i<tChart1.getLegend().getLegendItems().size(); i++) {
						Rectangle rect = tChart1.getLegend().getLegendItems().getLegendItem(i).getSymbolRect();
						
						if ((tChart1.getSeries(0) instanceof Custom) && (((Custom)tChart1.getSeries(0)).getPointer().getVisible())) {
							if (tChart1.getSeries(0).getColorEach()) {
								tChart1.getGraphics3D().getBrush().setColor(tChart1.getSeries(0).getColors().getColor(seriesOrder[i]));
							}
							else {
								tChart1.getGraphics3D().getBrush().setColor(tChart1.getSeries(0).getColor());
							}
							tChart1.getGraphics3D().rectangle(rect);
						}
						else {
							if (tChart1.getSeries(0).getColorEach()) {
								tChart1.getGraphics3D().getPen().setColor(tChart1.getSeries(0).getColors().getColor(seriesOrder[i]));
							}
							else {
								tChart1.getGraphics3D().getPen().setColor(tChart1.getSeries(0).getColor());
							}
							tChart1.getGraphics3D().horizontalLine(rect.x, rect.getRight(), (rect.y + rect.getBottom()) / 2);
						}
					}		
				}				
			}
		});

		tChart1.setLegendResolver(new LegendResolver() {
		
			@Override
			public String getItemText(Legend legend, LegendStyle legendStyle, int index, String text) {
				if (tChart1.getSeriesCount() > 1) {
					return tChart1.getSeries(seriesOrder[index]).titleOrName();
				}
				else {
					return String.valueOf(tChart1.getSeries(0).getYValues().getValue(seriesOrder[index]));	
				}
			}
			
			@Override
			public LegendItemCoordinates getItemCoordinates(Legend legend, LegendItemCoordinates coordinates) {
				return coordinates;
			}
			
			@Override
			public Rectangle getBounds(Legend legend, Rectangle rectangle) {
				return rectangle;
			}
		});
	}
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Jonathan
Newbie
Newbie
Posts: 60
Joined: Tue Mar 11, 2008 12:00 am
Location: Austin

Re: How to sort legend items along with its symbols

Post by Jonathan » Mon Apr 02, 2012 7:06 pm

Thanks for your help. It'd be great if your suggestion could be built-in APIs

Yeray
Site Admin
Site Admin
Posts: 9613
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: How to sort legend items along with its symbols

Post by Yeray » Tue Apr 03, 2012 7:47 am

Hi Jonathan,

The possibility to sort the legend items is already in the wish list to be implemented in future releases (TJ71014085)
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Jonathan
Newbie
Newbie
Posts: 60
Joined: Tue Mar 11, 2008 12:00 am
Location: Austin

Re: How to sort legend items along with its symbols

Post by Jonathan » Fri Apr 06, 2012 6:59 pm

Hi Yeray,

I have symbols on chart. When I play with the codes you suggested, I found that legend symbols doesn't get rendered properly. Could you tell me how to render the legend symbols correctly?
The example below, I added two series: one with START shape and the other with CIRCLE shape. After sorting, the shapes got covered by a rectangle.

Code: Select all

	public static void main(String[] args) {
		JFrame frame = new JFrame();
		JPanel panel = new JPanel();
		panel.setSize(600, 600);
		final TChart tChart1 = new TChart();
		panel.add(tChart1);
	
	    tChart1.getAspect().setView3D(false);
	    
	    	//add 2 line series
	      Line line2 = new Line(tChart1.getChart());
	      line2.add(1, 2);
	      line2.add(2, 3);
	      line2.add(3, 4);
	      line2.setColor(Color.RED);
	      line2.setTitle("STAR");
	      line2.getPointer().setStyle(PointerStyle.STAR);
	      line2.getPointer().setVisible(true);
	      line2.getPointer().getBrush().setColor(Color.RED);
	      line2.getPointer().getPen().setColor(Color.RED);
	      line2.setShowInLegend(true);
	      
	      Line line1 = new Line(tChart1.getChart());
	      line1.add(1.0, 1.0);
	      line1.add(2.0, 2.0);
	      line1.add(3.0, 3.0);
	      line1.setColor(Color.BLACK);
	      line1.setTitle("CIRCLE");
	      line1.getPointer().setStyle(PointerStyle.CIRCLE);
	      line1.getPointer().setVisible(true);
	      line1.getPointer().getBrush().setColor(Color.BLACK);
	      line1.getPointer().getPen().setColor(Color.BLACK);
	      line1.setShowInLegend(true);
	      
	      final int[] seriesOrder;
	      final boolean isSort = true;
	      
	      if (tChart1.getSeriesCount() > 1) {
	         seriesOrder = new int[tChart1.getSeriesCount()];
	         
	         for (int i=0; i<tChart1.getSeriesCount(); i++)
	            seriesOrder[i] = i;
	         
		      // sorting !!
	         if(isSort){
		         for (int i=0; i<seriesOrder.length; i++) {
		            for (int j=i+1; j<seriesOrder.length; j++) {
		               if (tChart1.getSeries(seriesOrder[j]).titleOrName().compareTo(tChart1.getSeries(seriesOrder[i]).titleOrName()) < 0) {
		                  int tmp = seriesOrder[i];
		                  seriesOrder[i] = seriesOrder[j];
		                  seriesOrder[j] = tmp;
		               }
		            }
		         }
	         }
	      }
	      else {
	         seriesOrder = new int[tChart1.getSeries(0).getCount()];
	         
	         for (int i=0; i<tChart1.getSeries(0).getCount(); i++)
	            seriesOrder[i] = i;
	         
	         for (int i=0; i<seriesOrder.length; i++) {
	            for (int j=i+1; j<seriesOrder.length; j++) {
	               if (tChart1.getSeries(0).getYValues().getValue(seriesOrder[j]) < tChart1.getSeries(0).getYValues().getValue(seriesOrder[i])) {
	                  int tmp = seriesOrder[i];
	                  seriesOrder[i] = seriesOrder[j];
	                  seriesOrder[j] = tmp;
	               }
	            }
	         }
	      }
	      
	      tChart1.addChartPaintListener(new ChartPaintAdapter() {
	         
	         @Override
	         public void chartPainted(ChartDrawEvent e) {
	            if (tChart1.getSeriesCount() > 1) {
	            	if(isSort){
		               for (int i=0; i<tChart1.getLegend().getLegendItems().size(); i++) {
		                  Rectangle rect = tChart1.getLegend().getLegendItems().getLegendItem(i).getSymbolRect();
		                  if ((tChart1.getSeries(seriesOrder[i]) instanceof Custom) && (((Custom)tChart1.getSeries(seriesOrder[i])).getPointer().getVisible())) {
		                     tChart1.getGraphics3D().getBrush().setColor(tChart1.getSeries(seriesOrder[i]).getColor());
		                     tChart1.getGraphics3D().getPen().setColor(tChart1.getSeries(seriesOrder[i]).getColor());
		                     tChart1.getGraphics3D().rectangle(rect);
		                  }
		                  else {
		                     tChart1.getGraphics3D().getPen().setColor(tChart1.getSeries(seriesOrder[i]).getColor());
		                     tChart1.getGraphics3D().horizontalLine(rect.x, rect.getRight(), (rect.y + rect.getBottom()) / 2);
		                  }
		               }
	            	}
	            }
	            else {
	               for (int i=0; i<tChart1.getLegend().getLegendItems().size(); i++) {
	                  Rectangle rect = tChart1.getLegend().getLegendItems().getLegendItem(i).getSymbolRect();
	                  
	                  if ((tChart1.getSeries(0) instanceof Custom) && (((Custom)tChart1.getSeries(0)).getPointer().getVisible())) {
	                     if (tChart1.getSeries(0).getColorEach()) {
	                        tChart1.getGraphics3D().getBrush().setColor(tChart1.getSeries(0).getColors().getColor(seriesOrder[i]));
	                     }
	                     else {
	                        tChart1.getGraphics3D().getBrush().setColor(tChart1.getSeries(0).getColor());
	                     }
	                     tChart1.getGraphics3D().rectangle(rect);
	                  }
	                  else {
	                     if (tChart1.getSeries(0).getColorEach()) {
	                        tChart1.getGraphics3D().getPen().setColor(tChart1.getSeries(0).getColors().getColor(seriesOrder[i]));
	                     }
	                     else {
	                        tChart1.getGraphics3D().getPen().setColor(tChart1.getSeries(0).getColor());
	                     }
	                     tChart1.getGraphics3D().horizontalLine(rect.x, rect.getRight(), (rect.y + rect.getBottom()) / 2);
	                  }
	               }      
	            }            
	         }
	      });

	      tChart1.setLegendResolver(new LegendResolver() {
	      
	         @Override
	         public String getItemText(Legend legend, LegendStyle legendStyle, int index, String text) {
	            if (tChart1.getSeriesCount() > 1) {
	               return tChart1.getSeries(seriesOrder[index]).titleOrName();
	            }
	            else {
	               return String.valueOf(tChart1.getSeries(0).getYValues().getValue(seriesOrder[index]));   
	            }
	         }
	         
	         @Override
	         public LegendItemCoordinates getItemCoordinates(Legend legend, LegendItemCoordinates coordinates) {
	            return coordinates;
	         }
	         
	         @Override
	         public Rectangle getBounds(Legend legend, Rectangle rectangle) {
	            return rectangle;
	         }


	      });
	         
		frame.add(panel);
		frame.setSize(600, 600);
		frame.setVisible(true);
	}

Yeray
Site Admin
Site Admin
Posts: 9613
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: How to sort legend items along with its symbols

Post by Yeray » Wed Apr 11, 2012 1:07 pm

Hi Jonathan,
Jonathan wrote:After sorting, the shapes got covered by a rectangle.
Note the code I suggested you is actually drawing that rectangles at chartPainted event:

Code: Select all

            @Override
            public void chartPainted(ChartDrawEvent e) {
               //...
               if (tChart1.getSeriesCount() > 1) {
                        //...
                        if ((tChart1.getSeries(seriesOrder[i]) instanceof Custom) && (((Custom)tChart1.getSeries(seriesOrder[i])).getPointer().getVisible())) {
                           //...
                           tChart1.getGraphics3D().rectangle(rect);
If you want to draw the symbol the series has, you should add some more logic. If you are source code customer, you can see at Custom.java, the drawLegendShape does something like:

Code: Select all

        if (getPointer().getVisible())
        {
            if (drawLine)
            {
                drawLine(g, false, tmpLegendColor, rect);
            }
            point.drawLegendShape(g, tmpLegendColor, rect, getLinePen().getVisible());
        }
And you can see at SeriesPointer.java, how drawLegendShape method is actually calling:

Code: Select all

        draw(g, false, (rect.x + rect.getRight()) / 2,
             (rect.y + rect.getBottom()) / 2,
             Math.min(horizSize, tmpHoriz),
             Math.min(vertSize, tmpVert), color, style);
However, there is an alternative that will probably be easier than all this manual drawing:
You could hide all the series from being drawn in the legend, but still calculating the correct order (using the method suggested above or any other you may think):

Code: Select all

          for (int i=0; i<tChart1.getSeriesCount(); i++) {
        	  tChart1.getSeries(i).setShowInLegend(false);
        	  seriesOrder[i] = i;
          }
And then, instead of the custom drawing routine and the legend texts customization, you could add clones of the series in the correct order:

Code: Select all

       for (int i=0; i<seriesOrder.length; i++) {
    	   Series s = tChart1.getSeries(seriesOrder[i]).cloneSeries();
    	   s.clear();
    	   s.setShowInLegend(true);
       }
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Jonathan
Newbie
Newbie
Posts: 60
Joined: Tue Mar 11, 2008 12:00 am
Location: Austin

Re: How to sort legend items along with its symbols

Post by Jonathan » Wed Apr 11, 2012 2:23 pm

Thanks for your feedback.

While waiting for your feedback, I actually come up with simpler solution, which is similar to the hiding series in the legend in your latest answer.

Code: Select all

	    	//add 2 line series
	      Line line2 = new Line(tChart1.getChart());
	      line2.add(1, 2);
	      line2.add(2, 3);
	      line2.add(3, 4);
	      line2.setColor(Color.RED);
	      line2.setTitle("STAR");
	      line2.getPointer().setStyle(PointerStyle.STAR);
	      line2.getPointer().setVisible(true);
	      line2.getPointer().getBrush().setColor(Color.RED);
	      line2.getPointer().getPen().setColor(Color.RED);
	      line2.setShowInLegend(true);
	      
	      Line line1 = new Line(tChart1.getChart());
	      line1.add(1.0, 1.0);
	      line1.add(2.0, 2.0);
	      line1.add(3.0, 3.0);
	      line1.setColor(Color.BLACK);
	      line1.setTitle("CIRCLE");
	      line1.getPointer().setStyle(PointerStyle.CIRCLE);
	      line1.getPointer().setVisible(true);
	      line1.getPointer().getBrush().setColor(Color.BLACK);
	      line1.getPointer().getPen().setColor(Color.BLACK);
	      line1.setShowInLegend(true);
	      
	      
	      Collection<Series> series = tChart1.getSeries();
	      List<Series> lineList = new ArrayList<Series>();
	      for(Series s : series){
	    	  lineList.add(s);
	      }
	      
	      //remove the original series
	      series.removeAll(lineList);
	      
	      // sort
	      Collections.sort(lineList, new Comparator<Series>(){

			@Override
			public int compare(Series o1, Series o2) {
				return o1.getTitle().compareTo(o2.getTitle());
			}
	    	  
	      });

	      series.addAll(lineList);

Yeray
Site Admin
Site Admin
Posts: 9613
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: How to sort legend items along with its symbols

Post by Yeray » Thu Apr 12, 2012 10:43 am

Hi Jonathan,

Right. Thanks for sharing! :)
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

cmpucom
Newbie
Newbie
Posts: 1
Joined: Mon Apr 19, 2004 4:00 am

Re: How to sort legend items along with its symbols

Post by cmpucom » Wed Oct 12, 2016 8:12 pm

I have about 10 series that needed in a specific order. Unlike my example, I could not be control the order of their creation.

* Programatically create the your series and put the series pointers in a collection, array, etc.

Code: Select all

    for TrcNum := 1 to 10 do begin
        TrcDta[TrcNum] := TLineSeries.Create(Nil);
        with TrcDta[TrcNum] do begin
            Color := DfltGphClr[TrcNum];
            Title := aTrcTitle;
            ParentChart   := Nil;    // important : don't define the owning TeeChart yet
            ShowInLegend  := true;
        end;
    end;
Sort your collection or traverse your array in the REVERSE order you want the items to appear in the legend.
The order ParentChart is defined sets the order of the items in the legend.

Code: Select all

    for TrcNum := 10 downto 1 do begin
        TrcDta[TrcNum].ParentChart := MyTeeChart;
    end;

Yeray
Site Admin
Site Admin
Posts: 9613
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: How to sort legend items along with its symbols

Post by Yeray » Fri Oct 14, 2016 2:35 pm

Hello,

Thanks for sharing to you too! :D
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Post Reply