1

I try to plot contour with only one cntrlabel per line but I do not succeed. I tried

set cntrlabel onecolor start 50 interval 10000000

and

set cntrlabel onecolor start 50 interval -1

but it does not work. Is there a mean to force 1 label per line ?

Moreover, I would like to shift the cntrlabel in order to prevent them to be overlayed (as observed on the top-left of the graph with the label 45, 50, 55, and 60). How should I do ?

enter image description here

The code used to obtain this graph is the following:

FILE = "data_sensibilite_correlation_phiFR_Tpfr_fusion_ordre"

set contour base
set cntrparam level discrete 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 25, 30, 35, 40, 45, 50, 55, 60
#set cntrparam level incremental 2, 4, 60
set cntrlabel onecolor start 50 interval 10000000

set xrange [-10:10]
set yrange [0.55:0.95]
#set cbrange [0:20]

set style textbox opaque
unset key
set view map
set xlabel "{/Symbol e}_{/Symbol q} [%]"
set ylabel "T_b / T_{c} uncertainties on T_c"
set cblabel "{/Symbol e}_{{/Symbol F} cs} [%]"

set pm3d noborder

splot FILE u 1:2:3 w pm3d, \
      FILE u 1:2:3 w l lc "black" nosurface, \
            FILE with labels boxed

The data is available here: https://filesender.renater.fr/?s=download&token=c718b69b-1496-47db-9da4-21d48cf08aa4

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Martin7
  • 93
  • 8
  • as far as I understand, individual contour lines (although they are looking like one line) might actually consist out of several pieces. That's probably why `interval` or (if you had them in a dataset) `every` will not work that smoothly. Either there are some settings (which I am not aware of right now) or it might require some workaround. – theozh Jan 28 '21 at 08:44
  • There's a gnuplot tricks blog post on the topic, which indeed uses a workaround: https://gnuplot-tricks.blogspot.com/2009/07/maps-contour-plots-with-labels.html – Joce Jan 28 '21 at 17:30

2 Answers2

0

Some time ago, I experienced the same issue. Since your data is not accessible anymore, I am creating some test data in the script itself.

The problem is that you see too many labels although you are trying to limit them via set cntrlabel {start <int>} {interval <int>}. Let me try to explain: If you plot the contour line into a datablock you will notice that although some contour lines in the graph look like continuous lines, however, in fact, they are interrupted by empty lines in the data. I guess this comes from the algorithm how gnuplot determines the contour lines. It seems, the more data points you have, the higher the probability that some contour lines are interrupted.

In the first graph below I made the interruptions visible by setting variable line color depending on pseudocolumn -1 (check help pseudocolumns). For the time being ignore the yellow line. For example, the contour line for the level 8 consists of 5 pieces (i.e. 5 different colors). This means when plotting the contour labels even if you set every 200 (or interval 10000000) you will get at least as many labels per contour line as many "broken" parts you have for that line.

Test Graph:

enter image description here

So, you could try to merge these interrupted lines which might be feasible, however, is not so easy because:

  • you have contour lines which should not be merged, e.g. level 15 on the left side and on the right side of the graph
  • you cannot easily connect the interrupted lines by simply removing the empty lines because the data points of the line parts are not in the right order

A different approach:

Hence, here is another "simple" idea, but not so simple to realize:

Define a parametric curve (yellow line in the above graph) which will intersect all the contour lines which you want to have labeled. The script will determine the intersection points and will place a label at these positions. The determination of the intersections is somewhat lengthy and the code is taken from here.

This procedure is certainly slow and not very efficient because it checks each yellow segment against all other segments. Currently, the sampling of the yellow line needs to be high enough (here: N=21) such that each yellow segment will intersect with one contour line segment. The calculation time for the intersections can probably be shortened considerably if one yellow segment can intersect several contour line segments. Alternatively, the intersection lines could be filtered by level and then intersected. I will try these options asap. If anyone has a more ideas to improve, please let me know.

Script:

### add contour labels nicely aligned
reset session

# create some test data
f(x,y) = ((4*x)**2 + (-y-5)**2)/10.
set samples    200
set isosamples 200
set table $Data
    splot '++' u (x):(y):(f(x,y))
set table $Contours
    unset surface
    set contour
    set cntrparam levels discrete 2,4,6,8,10,15,20,25,30,40
    splot $Data u 1:2:3
unset table

set colorsequence classic
set style textbox opaque 
set key noautotitle
set view map

# define parametric function for "line of labels"
xmin  = -5.5
xmax  =  5.5
N     = 21
g(x)  = 0.25*x**2 - 2
gx(t) = xmin + t*(xmax-xmin)/N
gy(t) = g(gx(t))
set table $LabelLine
    plot '+' u (gx($0)):(gy($0)) every ::::N w table
unset table

set xrange [:] noextend
set yrange [:] noextend

# this plotting part can be skipped, it's just for illustration purpose
plot $Contours  u 1:2:-1 w l lc var, \
             '' u 1:2:3 every 200 w labels boxed, \
     $LabelLine u 1:2 w lp pt 7 lc "yellow" noautoscale
pause -1

# some necessary functions
# orientation of 3 points a,b,c: -1=clockwise, 0=linear, +1=counterclockwise
Orientation(a,b,c) = sgn((word(b,1)-word(a,1))*(word(c,2)-word(a,2)) - \
                         (word(c,1)-word(a,1))*(word(b,2)-word(a,2)))

# check for intersection of segment a-b with segment c-d,
# 0=no intersection, 1=intersection
IntersectionCheck(a,b,c,d) = \
    (Orientation(a,c,b)==Orientation(a,d,b)) || (Orientation(c,a,d)==Orientation(c,b,d)) ? 0 : 1

# calculate coordinates of intersection point, "" if identical points
M(a,b) = real(word(a,1)*word(b,2) - word(a,2)*word(b,1))
N(a,b,c,d) = (word(a,1)-word(b,1))*(word(c,2)-word(d,2)) - \
             (word(a,2)-word(b,2))*(word(c,1)-word(d,1))
Intersection(a,b,c,d) = N(a,b,c,d) !=0 ? sprintf("%g %g", \
    (M(a,b)*(word(c,1)-word(d,1)) - (word(a,1)-word(b,1))*M(c,d))/N(a,b,c,d), \
    (M(a,b)*(word(c,2)-word(d,2)) - (word(a,2)-word(b,2))*M(c,d))/N(a,b,c,d)) : ""

# looping data segments for finding intersections
set print $Intersections
do for [i=1:|$LabelLine|-1] {
    a = sprintf("%s %s", word($LabelLine[i],  1),word($LabelLine[i],  2))
    b = sprintf("%s %s", word($LabelLine[i+1],1),word($LabelLine[i+1],2))
    Line = ''
    Intersection0 = ''
    do for [j=1:|$Contours|-1] {
        c = $Contours[j]
        d = $Contours[j+1]
        if (strlen(c)!=0 && strlen(d)!=0 && c[1:1] ne '#' && c[1:1] ne '#') {
            if (IntersectionCheck(a,b,c,d)) {
                Intersection1 = Intersection(a,b,c,d)
                if ((Intersection0 ne Intersection1)) {
                    Level = word($Contours[j],3)
                    print sprintf("%s %s %s",Intersection0, Intersection1, Level)
                }
                Intersection0 = Intersection1
            }
        }
        else {Intersection0 = ''}
    }
}
set print

set palette rgb 33,13,10

plot $Data          u 1:2:3 w image, \
     $Contours      u 1:2 w l lc "black", \
     $Intersections u 1:2:3 w labels boxed 
### end of script

Result:

enter image description here

theozh
  • 22,244
  • 5
  • 28
  • 72
0

Here is a much simpler solution resulting in only one label per level, however, depending on your data you won't know where exactly the labels will be positioned.

The contour lines per level are separated by two empty lines. The different pieces within a contour line of one specific level might be separated into (sub-)blocks separated by a single empty line.

Now, you can address specific rows and sub-blocks via every (check help every). For example, if you only want to plot each second row of each first sub-block you can specify every ::1:0:1:0 (indices are zero-based). You need to play with these numbers, depending on your data and how many contour line breaks you have. However, most likely the labels will not be nicely aligned as in my other (much more complicated) answer. Furthermore, the labeling will be only once per level, i.e. no labels on the left side of the plot in the example below.

Script:

### add contour labels, only one per level
reset session

# create some test data
f(x,y) = ((4*x)**2 + (-y-5)**2)/10.
set samples    200
set isosamples 200
set table $Data
    splot '++' u (x):(y):(f(x,y))
set table $Contours
    unset surface
    set contour
    set cntrparam levels discrete 2,4,6,8,10,15,20,25,30,40
    splot $Data u 1:2:3
unset table

set colorsequence classic
set style textbox opaque 
set key noautotitle
set view map

set xrange [:] noextend
set yrange [:] noextend
set tics out

set palette rgb 33,13,10

plot $Data          u 1:2:3 w image, \
     $Contours      u 1:2 w l lc "black", \
     $Contours      u 1:2:3 every ::1:1:1:1 w labels boxed 
### end of script

Result:

enter image description here

theozh
  • 22,244
  • 5
  • 28
  • 72