How to add contour lines to a heat map - plot

I have a script which takes data (formatted in 3 columns x,y,z) and gives a heat map:
set logscale x 10
set yrange [1e-9:2e-8]
set xlabel "x"
set ylabel "y"
set multiplot
plot 'filetest.dat' u 1:2:9 with image
This is a 2D heat map, shown below:
All I want to do is add contours to this plot, at some z values such as -20 to -8 in in intervals of 2. Unfortunately, none of the answers I've found have been able to help me with this. Any help would be greatly appreciated.

Although there are a lot of examples about contour on www.gnuplot.info, I couldn't find your exact case, because the examples are with functions, not with datablocks or data files (well, it should be similar).
The following code does what you're asking for, but the construct '' u 1:2:3:("") w labels for adding labels still looks strange to me and doesn't allow for plotting boxed labels.
In gnuplot console check help contour and help cntrparam.
Code:
### pm3d with contour lines
reset session
set view equal xyz
# create some test data
set samples 40
set isosamples 40
set table $Data
splot '++' u 1:2:($1*$2/2-9)
unset table
set view map
set contour base
set cntrparam levels incremental -20,2,-8
set cntrlabel font ",10"
set xrange[-5:5]
set yrange[-5:5]
splot $Data u 1:2:3 w pm3d notitle, '' u 1:2:3:("") w labels notitle
### end of code
Result:
Addition:
Here is another approach with plot w image instead of splot w pm3d.
Although still not fully satisfying with the white label boxes on top of the contour lines. Adding an offset to the labels will not work for all labels at the same time. I'm not sure whether there is a way to just interrupt the contour lines for the labels.
Code:
### heatmap with contour lines
reset session
set view equal xyz
# create some test data
set samples 40
set isosamples 40
set table $Data
splot '++' u 1:2:($1*$2/2-9)
unset table
set view map
set contour base
set cntrparam levels incremental -20,2,-8
set cntrlabel font ",10"
set xrange[-5:5]
set yrange[-5:5]
set style textbox noborder opaque
# put contour lines in a separate datablock
unset surface
set table $Contour
splot $Data u 1:2:3
unset table
plot $Data u 1:2:3 w image notitle, \
$Contour u 1:2 w l lw 2 lc "black" not, \
'' u 1:2:3 every 40::3 w labels boxed notitle
### end of code
Result:
Addition 2:
Another variation with colored contour lines and key instead of labels. This seems to be a bit cumbersome, I hope there is a simpler solution for this.
Code:
### heatmap with colored contour lines
reset session
set view equal xyz
# create some test data
set samples 40
set isosamples 40
set table $Data
splot '++' u 1:2:($1*$2/2-9)
unset table
set view map
set contour base
set cntrparam levels incremental -20,2,-8
set xrange[-5:5]
set yrange[-5:5]
set style textbox noborder
# put contour lines in a separate datablock
unset surface
set table $Contour
splot $Data u 1:2:3
unset table
# get contour levels unique and in sorted order
set table $Dummy
plot $Contour u 3 w table
unset table
set table $ContourSorted
plot $Dummy u 1 smooth freq
unset table
print $ContourSorted
set key out right Left
plot $Data u 1:2:3 w image notitle, \
for [i=0:*] $Contour u 1:2:3 index i w l lw 2 lc i+1 not, \
for [i=|$ContourSorted|-2:5:-1] $ContourSorted u (NaN):1 w l lw 2 lc |$ContourSorted|-i-1 ti word($ContourSorted[i],1)
### end of code
Result:

Related

cntrlabel contour plot

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 ?
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
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:
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:
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:

splot with mixing data and parameter

I have two curves as data points (i. e. a two sets of two-tuples). I want to splot the surface of their weighted sum, the weight being the third axis (so like a smooth transition from one curve to the other).
Example: If I have the functions sin(x) and x**2 / 100, I can achieve it like this:
set isosamples 100
splot [-10:10] [0:1] y * sin(x) + (1-y) * (x**2 / 100)
In my case, however, I do not have functions but values from a data file and I do not know how to combine this with an automatic running value like the weight y in the example above. I tried, e.g. this, but it did not work:
splot [] [0:1] 'datafile' using 1:(y):(y * $2 + (1-y) * $3)
The error I get is undefined variable: y (which is clear). I just don't know how to combine data from a data file and a running parameter.
The first idea which comes to my mind is to plot the "mixed" data into a table. I hope that there are better approaches.
Code:
### mixing of parameter and data
reset session
# create some test data
set table $Data
plot '+' u 1:(sin($1)):($1**2/100.) w table
unset table
N = 20.0 # float number to avoid integer division
set table $Mix
do for [i=0:N] {
plot $Data u 1:(i/N):(i/N*$2 + (1-i/N)*$3) w table
plot '+' u ("") every ::0::0 w table # plot "empty line" to disconnect lines
}
unset table
set view 48,9
set ztics 0.5
splot $Mix u 1:2:3 w l
### end of code
Result:
If you need only a smooth transition, you can use dgrid3d
set table $Data
plot '+' u 1:($1**2/100.) w table
plot '+' u ("") every ::0::0 w table # plot "empty line" to disconnect lines
plot '+' u 1:(sin($1)) w table
unset table
set view 48,9
set ticslev 0
set dgrid3d 20,100 splines
splot $Data us 1:-1:2 w l
The pseudocolumn -1 indexes the data set.
If your x data are uniform sampled, you can simplify this to
set table $Data
plot '+' u ($1**2/100.):(sin($1)) w table
unset table
set dgrid3d 20,100 splines
splot $Data matrix us ($2/10-5):1:3 w l

gnuplot splot with multiple data sets

I have a script that generates a gnuplot file, I have edited so I have 2 sets of data in the same plot. One is multiplied by -1 to change color. However, only one set appears in the plot, the last one stated. The script is the following.
set arrow from graph 0,first 0.61237, graph 0 to graph1,first0.61237,0 nohead front lt -1
set arrow from graph 0,first 1.67303, graph 0 to graph 1,first 1.67303,0 nohead front lt -1
set arrow from graph 0,first 2.53906, graph 0 to graph 1,first 2.53906,0 nohead front lt -1
set arrow from graph 0,first 3.24616, graph 0 to graph 1,first 3.24616,0 nohead front lt -1
set arrow from graph 0,first 3.74616, graph 0 to graph 1,first 3.74616,0 nohead front lt -1
set arrow from graph 0,first 4.74616, graph 0 to graph 1,first 4.74616,0 nohead front lt -1
set pm3d at b
unset surf
set view 180,90
set palette defined (-1 "red",0 "white", 1 "blue");
set grid xtics mxtics ytics
unset border
set xrange [-10:40]
set xtics -10,((40+10)/5),40
set mxtics 3
set yzeroax lt 2
set ytics ("L" 0.0,"K" 0.61237,"G" 1.67303,"L" 2.53906,"W" 3.24616,"X" 3.74616,"G" 4.74616)
set cbrange resto
set cbrange[-1:1]
set colorbox
set terminal png enhanced nocrop size 1920,1080
set out "SPF_NO_SO.png"
splot "specfun.pm3d" u ($1*13.60569806):2:(1*$4/476.70750366666666666666),\
"specfun.pm3d" u ($1*13.60569806):2:(1*$3/573.04673900000000000000)
unset out
Sample image:
The script produces the desired plot only for the negative set. If I flip the order I get the blue one.
Here is a sample data:
data_sample
FYI-these arrays are quite big
as #user8153 pointed out, I also don't see a reason why using splot.
And as #Christoph pointed out it's difficult without knowing how the data looks like,
for example, what's in columns $3 and $4?
By the way, with your plot command ...u ($1*13.60569806):2 aren't you plotting the same function twice on top of each other, i.e. that's why you see either the red or the blue?
Nevertheless, I tried to anticipate what you probably want to do. I scaled the second function and assumed something linear for columns $3 and $4. Please clarify.
With the code:
### start code
reset session
# generate some dummy data
undefine $Data
set print $Data append
do for [n=1:5] {
a = rand(0)*10
b = rand(0)*0.1+0.1
c = rand(0)*20
do for [i=1:100] {
print sprintf("%g\t%g\t%g\t%g",i,(a*sin(b*i-c)),cos(b*i),cos(b*3*i))
}
print "" # insert empty line for data curve separation
}
set print
# print $Data
# end generating dummy data
set palette defined (-1 "red",0 "white", 1 "blue")
set cbrange [-1:1]
plot $Data u 1:2:($3-$4) w l lc palette z
### end of code
You'll get the following:

Gnuplot , pm3d with contour lines

i am 3d plotting a matrix with some values, and i need to add contour lines to the plot, is there a simple gnuplot command to do this?
I tried the command: "set contour base" but only 1 line came up, i think it should be many lines. See matlab picture
When i plot it in gnuplot i only get 1 contour line in the top left corner.But everything else is correct.
My goal is to get it to look like in matlab like this Matlabplot
I also found this example: see link in comments (dont have enough rep), but i dont understand where i should put in the data values from test.txt
test.txt
test.txt
gnuplot commands
set view map
set yrange [0:30]
set xrange [0:30]
set dgrid3d 100,100,4
set contour base
splot 'test.txt' u 1:2:3 w pm3d
What you are missing is to tell gnuplot where to put the contours. This is done via the set cntrparam levels incr -0.3,0.1,0.5 command which means: start at -0.3 and trace a contour every o.1 up to 0.5.
AFAIK if you want to make contours all black, you have to save the contour lines in a temporary file (here contour.txt).
So your script would be
reset
set contour
unset surface
set cntrparam levels incr -0.3,0.1,0.5
set view map
set xrange [0:30]
set yrange [0:30]
set dgrid3d 100,100,4
set table "contour.txt"
splot 'test.txt'
unset table
unset contour
set surface
set table "dgrid.txt"
splot 'test.txt'
unset table
reset
set pm3d map
unset key
set palette defined (0 '#352a87', 1 '#0363e1',2 '#1485d4', 3 '#06a7c6', 4 '#38b99e', 5 '#92bf73', 6 '#d9ba56', 7 '#fcce2e', 8 '#f9fb0e')
set autoscale fix
set grid
splot 'dgrid.txt' w pm3d, 'contour.txt' w l lc rgb "black"
which gives you this:
Note:
you can get rid of interpolation file (dgrid.txt) if you format a bit your datafile by leaving a blank line after each row (i.e. every 30 datapoints) because they are already mesh-ordered.
This could be done also with a awk script. But I'm too lazy to look into it...
The rest will remain the same and will work as expected.
here is how it should look like :
In which case the script would simply become:
set pm3d map impl
set contour
set style increment user
do for [i=1:18] { set style line i lc rgb "black"}
set cntrparam levels incr -0.3,0.1,0.5
set palette defined (0 '#352a87', 1 '#0363e1',2 '#1485d4', 3 '#06a7c6', 4 '#38b99e', 5 '#92bf73', 6 '#d9ba56', 7 '#fcce2e', 8 '#f9fb0e')
set autoscale fix
splot 'test.txt' w pm3d notitle
with no need of ntermediate file and with better contour since data in not interpolate by gridded:

Plotting a contour and a line together in gnuplot

I am trying to plot a contour from 3D data, where the contour will only on the base
set contour base
set cntrparam bspline
unset surface
set view map
splot 'file1.dat' u ($1):($2):($3) w l ls 1 notitle, \
'file2.dat' u ($2):($3):(0) w lp ls 2
The second file is just a line which I want to plot in X-Y plane. However, since the surface is unset, the second plot does not show up. If I remove unset surface I see unwanted lines from the first file. Is there a solution to it?
It probably works (I don't have data to test with) if you use nosurface for the first and nocontour for the second plot:
set contour base
set cntrparam bspline
set view map
splot 'file1.dat' u 1:2:3 ls 1 w l nosurface notitle, \
'file2.dat' u 2:3:(0) w lp ls 2 nocontour

Resources