Page 1 of 1

How to sort legend items along with its symbols

Posted: Tue Mar 27, 2012 8:26 pm
by 7667590
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?

Re: How to sort legend items along with its symbols

Posted: Thu Mar 29, 2012 12:04 pm
by yeray
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;
			}
		});
	}

Re: How to sort legend items along with its symbols

Posted: Mon Apr 02, 2012 7:06 pm
by 7667590
Thanks for your help. It'd be great if your suggestion could be built-in APIs

Re: How to sort legend items along with its symbols

Posted: Tue Apr 03, 2012 7:47 am
by yeray
Hi Jonathan,

The possibility to sort the legend items is already in the wish list to be implemented in future releases (TJ71014085)

Re: How to sort legend items along with its symbols

Posted: Fri Apr 06, 2012 6:59 pm
by 7667590
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);
	}

Re: How to sort legend items along with its symbols

Posted: Wed Apr 11, 2012 1:07 pm
by yeray
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);
       }

Re: How to sort legend items along with its symbols

Posted: Wed Apr 11, 2012 2:23 pm
by 7667590
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);

Re: How to sort legend items along with its symbols

Posted: Thu Apr 12, 2012 10:43 am
by yeray
Hi Jonathan,

Right. Thanks for sharing! :)

Re: How to sort legend items along with its symbols

Posted: Wed Oct 12, 2016 8:12 pm
by 9337126
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;

Re: How to sort legend items along with its symbols

Posted: Fri Oct 14, 2016 2:35 pm
by yeray
Hello,

Thanks for sharing to you too! :D