Get color by value from a palette drawn with "COL" option

Hi,

I have a 2D histogram drawn with the “COL” option like e.g. this one:

Now I want to programmatically know the color that corresponds to e.g. z=10. How do I do that?

I know there is an Int_t TPaletteAxis::GetValueColor(Double_t zc), but apparently it only works if there is a palette axis present. That is, I need to draw my histogram with the “COLZ” option instead of “COL”, which does not really suit my needs. I also understand that I could draw my histogram with “COLZ”, then move the palette axis outside the visible portion of the canvas and to proceed with TPaletteAxis::GetValueColor(...), but this seems unnecessarily complicated.

So I was wondering if there is another way of accomplishing it. If not (yet), why is that we need a palette axis to be able to get the color of the palette? It sounds to me like this has to be a property of a palette, not its axis. An axis may or may not be present, but the palette is present anyways.

Maybe this is something that could be considered for the future versions of ROOT?

Thanks!


Please read tips for efficient and successful posting and posting code

Please fill also the fields below. Note that root -b -q will tell you this info, and starting from 6.28/06 upwards, you can call .forum bug from the ROOT prompt to pre-populate a topic.

ROOT Version: 6.30/02
Platform: alma9
Compiler: does not matter


Yes most simple way is to display the palette and use GetValueColor. I will check how to do it without a palette. brb

The following example does what you are looking for. It does not even need to draw the histogram. The only thing to be defined is the number of contours. The graphical part of the code if there only to check that the index found is the right one.

int valuecolor(TH2D *h, double v) {
   Double_t zmin  = h->GetMinimum();
   Double_t zmax  = h->GetMaximum();
   int ncolors = gStyle->GetNumberOfColors();
   int ndivz = h->GetContour();
   if (ndivz == 0) return 0;
   ndivz = TMath::Abs(ndivz);
   int theColor, color;
   double scale = ndivz / (zmax - zmin);
   color = int(0.01 + (v - zmin) * scale);
   theColor = int((color + 0.99) * Double_t(ncolors) / Double_t(ndivz));
   return gStyle->GetColorPalette(theColor);
}

void getvaluecolor3(double v=8.) {
   auto hist  = new TH2D("hist", "", 10, -5, 5, 10, -5, 5);
   hist->SetContour(50);
   
   for(int i = 0; i < 100; i++){
      double x = gRandom->Gaus();
      double y = gRandom->Gaus();
      hist->Fill(x, y);
   }
   int ci = valuecolor(hist,v);
   printf("color index for %g = %d\n",v, ci);

   // visually check if the color found is the right one
   hist->Draw("COLZ");
   TLatex *   tex = new TLatex(-4.46,-4.,Form("This text is drawn with color corresponding to %g",v));
   tex->SetTextColor(ci);
   tex->Draw();
}
root [0] .x getvaluecolor3.C(1.)
color index for 1 = 944

Fantastic, thanks so much Olivier!

I noticed you have
hist->SetContour(50);
in your void getvaluecolor3(...)
– this line makes my own palette (as defined in a file linked from my ~/.rootlogon.C) more discrete than usual. I don’t understand how to use my own palette with this macro: as soon as I comment out this hist->SetContour(50);, your macro returns a color index of 0 for any v.

There only one palette active at a time in ROOT. So your own palette will be use. The number of contours need to be defined if you do not plot the histogram. If you plot it with COL before calling valuecolor then it should be defined for you. You can specify the value you want. The default number of contour is 20 at plotting time if you do not specify anything.

My issue is that when I use my own palette without doing

hist->SetContour(50);

, apparently

if (ndivz == 0) return 0;

from your int valuecolor(...) is satisfied and ci equals zero for any v. I’m working on a minimal standalone reproducer…

Ah, I got it now: your macro works fine for me if I replace

hist->SetContour(50);

with

hist->SetContour(gStyle->GetNumberContours());

Many thanks again!

It is the same with the standard palette. As i said the number of contour needs to be feined because when an histogram is drawn with option COL not all the colors present in the palette are used. Only a number equald to the number of contours is used. When you do h->Draw(“COL”) the number of contour is automatically defined. But if (like in my example) you do not draw the histogram you need to define it as h->Draw(“COL”) would do.

The simplest is may be to remove SetContour from the main contour and put it in valuecolor:

int valuecolor(TH2D *h, double v) {
   double zmin = h->GetMinimum();
   double zmax = h->GetMaximum();
   int ncolors = gStyle->GetNumberOfColors();
   int ndivz   = h->GetContour();
   if (ndivz == 0) {
      ndivz = 20;
      h->SetContour(ndivz);
   }
   ndivz = TMath::Abs(ndivz);
   int theColor, color;
   double scale = ndivz / (zmax - zmin);
   color = int(0.01 + (v - zmin) * scale);
   theColor = int((color + 0.99) * Double_t(ncolors) / Double_t(ndivz));
   return gStyle->GetColorPalette(theColor);
}

void getvaluecolor3(double v=8.) {
   auto hist  = new TH2D("hist", "", 10, -5, 5, 10, -5, 5);

   for(int i = 0; i < 100; i++){
      double x = gRandom->Gaus();
      double y = gRandom->Gaus();
      hist->Fill(x, y);
   }
   int ci = valuecolor(hist,v);
   printf("color index for %g = %d\n",v, ci);

   // check the color found is the right one
   hist->Draw("COLZ");
   TLatex *   tex = new TLatex(-4.46,-4.,Form("This text is drawn with color corresponding to %g",v));
   tex->SetTextColor(ci);
   tex->Draw();
}

Again, this changes my palette making it more discrete. I’d like to avoid this. Here is my reproducer: it is based on your last macro (where you have SetContour inside the int valuecolor(...) function), but I added my own palette I had defined in a file linked from my ~/.rootlogon.C. To run, just do

root -n 'getvaluecolor5.cxx(6)'

Here is the content of getvaluecolor5.cxx:

int valuecolor(TH2D *h, double v) {
   double zmin = h->GetMinimum();
   double zmax = h->GetMaximum();
   int ncolors = gStyle->GetNumberOfColors();
   int ndivz   = h->GetContour();
   if (ndivz == 0) {
      ndivz = 20;
      h->SetContour(ndivz);
   }
   ndivz = TMath::Abs(ndivz);
   int theColor, color;
   double scale = ndivz / (zmax - zmin);
   color = int(0.01 + (v - zmin) * scale);
   theColor = int((color + 0.99) * Double_t(ncolors) / Double_t(ndivz));
   return gStyle->GetColorPalette(theColor);
}

void getvaluecolor5(double v=8.) {

// my own palette
   gStyle->SetPalette(1);
   constexpr Int_t NCont {255};
   Double_t stops[] {0.00, 0.34, 0.61, 0.84, 1.00};
   Double_t red[]   {0.00, 0.00, 0.87, 1.00, 0.51};
   Double_t green[] {0.00, 0.81, 1.00, 0.20, 0.00};
   Double_t blue[]  {0.51, 1.00, 0.12, 0.00, 0.00};
   TColor::CreateGradientColorTable(std::size(stops), stops, red, green, blue, NCont);
   gStyle->SetNumberContours(NCont);


   auto hist  = new TH2D("hist", "", 10, -5, 5, 10, -5, 5);

   for(int i = 0; i < 100; i++){
      double x = gRandom->Gaus();
      double y = gRandom->Gaus();
      hist->Fill(x, y);
   }
   int ci = valuecolor(hist,v);
   printf("color index for %g = %d\n",v, ci);

   // check the color found is the right one
   hist->Draw("COLZ");
   TLatex *   tex = new TLatex(-4.46,-4.,Form("This text is drawn with color corresponding to %g",v));
   tex->SetTextColor(ci);
   tex->Draw();
}

Simply set a bigger value for the number of contours.

Yup, that does the trick!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.