Contour lines with labels in heatmap with GNUplot

邮差的信 提交于 2019-12-12 04:32:34

问题


Following this question, I have now a heatmap with custom colorbar. Now I need to put contour lines with labels. I have created the contour lines, but I can't figure out how to put them.

This is the plot I currently have:

Code to generate the plot:

reset
#Function map values on desired ranges. The values set to 90,95,99 are for later labelling
step(x) = (x < 20.0  ? 0 :  (x < 50.0  ? 10 :  (x < 75.0  ? 20 : (x < 90.0  ? 80 : (x < 95.0  ? 90 : (x < 97.5 ? 95 :  (x < 99.0 ? 97 : (x < 99.5  ? 99 : (x < 99.9 ? 100 : 101 )))))))))

# enable 3D data read from a scattered data set in file
#71,46,10 are the number of different values for each axis X,Y,Z in "HeatMap_Test.txt" data file
#If values of dgrid3d are not set accordingly, weird contour values will be generated
set dgrid3d 71,46,10
set contour base # enable contour drawing
set cntrlabel font ",7"
set view 0,0 # if you want to see the graph from above (2D plot)
unset surface # do not plot the surface, only the contour
set cntrparam levels discrete 90,95,99 #set contours only on values 90,95 and 99
set table "contours.dat" #Name of the output file to write the table
splot "HeatMap_Test.txt" u 2:1:(step($3)) with lines notitle
unset table  #Write table to file


reset
#Map the Z values to desired ranges
step(x) = (x < 20.0  ? 0 :  (x < 50.0  ? 1 :  (x < 75.0  ? 2 : (x < 90.0  ? 3 : (x < 95.0  ? 4 : (x < 97.5 ? 5 :  (x < 99.0 ? 6 : (x < 99.5  ? 7 : (x < 99.9 ? 8 : 9 )))))))))

set palette maxcolors 10
set palette defined (0 "#333399", 1 "#3333f7", 2 "#3373ff", 3 "#33c6ff", 4 "#5affd2", 5 "#9cff8f", 6 "#dfff4c", 7 "#ffc733", 8 "#ff7a33", 9 "#e53333")

set cbrange [-0.5:9.5]
set cbtics nomirror
set cbtics ( ">99.9" 9, ">99.5" 8, ">99.0" 7, ">97.5" 6, ">95.0" 5, ">90.0" 4, ">75.0" 3, ">50.0" 2, ">20.0" 1, ">10.0" 0 )

set xrange [-30:40]
set yrange [ 25:70]
set xtics 5
set ytics 5
#set title "                                                                                                                                       %"

set grid front linetype -1
set grid xtics lt 0 lw 1 lc rgb "#000000"
set grid ytics lt 0 lw 1 lc rgb "#000000"

plot "HeatMap_Test.txt" u 2:1:(step($3)) notitle pt 5 ps 2 lc palette, "world_10m.txt" notitle with lines ls 1 lc -1, "contours.dat" u 1:2 w l lw 2 lc 0 notitle

Here are the data files: HeatMap_Test.txt, world_10m.txt

The code works in GNUplot 4.6 and 5 (I am working under Linux)

To get the plot I want, I still have to do three things:

1) Label the contourlines (with 99, 95 and 90 from the inner to the outer lines)

2) Make the borderlines fit the plot (in the figure the heatmap goes over the borders)

3) Put a title (in this case a single '%') just on top of the colorbar. A dirty way to do it is to set a normal title and put spaces (there is a commented line in the code to do it), but I think there must be something better.

My idea of labelling the contour is like in this image:

Thank you for your help


回答1:


Let's start with number 3: A title for the colorbar.

You want to set the cblabel on top of the colorbar. While I don't think that this can be done automatically, you can use an offset x,y like this:

set cblabel "%" norotate offset -6, 9

Still a hack, but I think it is better than misusing the title.

Now number 2: Make the borderlines fit the plot.

The reason for going over the borderline is the (implicit) command plot with points pointsize 2. The points which are plotted just on the border will overlap. I would suggest to replace the points with a splot pm3d like this:

set pm3d explicit
set view map
splot "HeatMap_Test_2.txt" u 2:1:(0):(step($3)) notitle w pm3d lc palette , \
      "world_10m.txt" u 1:2:(0) notitle with lines ls 1 lc rgb "#ffffff" , \
      "contours.dat" u 1:2:(0) w l lw 2 lc rgb "#000000" notitle

To get this 3D splot to work, I did the following:

  • add a dummy z column, the number (0)
  • format "HeadMap_test.txt" into blocks, which means to insert a blank line after each latitude scan

from:

...
25.00   38.00   15.9
25.00   39.00    5.3
25.00   40.00    1.6
26.00  -30.00    0.0
26.00  -29.00    0.3
26.00  -28.00    0.7
...

to:

...
25.00   38.00   15.9
25.00   39.00    5.3
25.00   40.00    1.6

26.00  -30.00    0.0
26.00  -29.00    0.3
26.00  -28.00    0.7
... 
  • And I had to adjust the first step function for finding the contours a little bit, I have decreased the values of the steps at 90, 95, and 99 a little bit

Like this:

step(x) = (x < 20.0 ? 0    : \
          (x < 50.0 ? 10   : \
          (x < 75.0 ? 20   : \
          (x < 90.0 ? 80   : \
          (x < 95.0 ? 89.999 : \
          (x < 97.5 ? 94.999 : \
          (x < 99.0 ? 97   : \
          (x < 99.5 ? 98.999 : \
          (x < 99.9 ? 100  : 101 )))))))))

Now, with pngcairo terminal and Gnuplot 4.6, we should have arrived here:

And number 1: Label the contourlines

I have not tried it, but maybe this post can help you.




回答2:


I finally got it, thanks to the answer of @maij, using the link he provided.

With issue number 3, I used @maij line with this parameters:

set cblabel "%" norotate offset -8, 12

With issue number 2, when plotting the heatmap, I had to change from

plot "HeatMap_Test.txt" u 2:1:(step($3)) notitle pt 5 ps 2 lc palette

to

plot "HeatMap_Test.txt" u 2:1:(step($3)) with image

Last, with issue number 1, to put the labels in the contour lines, I had to use an external awk script provided in the same link as before. I named the script draw_contourlines_label.sh, and the content of the script is:

#!/bin/bash

awk -v d=$2 -v w=$3 -v os=$4 'function abs(x) { return (x>=0?x:-x) }
    {
         if($0~/# Contour/) nr=0
         if(nr==int(os+w/2) && d==0) {a[i]=$1; b[i]=$2; c[i]=$3;}
         if(nr==int(os+w/2)-1 && d==0) {i++; x = $1; y = $2;}
         if(nr==int(os+w/2)+1 && d==0) r[i]= 180.0*atan2(y-$2, x-$1)/3.14
         if(abs(nr-os-w/2)>w/2 && d==1) print $0
         nr++
    }
    END {   if(d==0) {
             for(j=1;j<=i;j++)
                printf "set label %d \"%g\" at %g, %g centre front rotate by %d\n", j, c[j], a[j], b[j], r[j]
             }
     }' "$1"

This script has the following parameters:

#1 -> File with the contour data generated by GNUplot

#2 -> Flag. If it is '0', prints a script for GNUplot to add the labels in the plot. If it is '1', takes out the points of the contour lines so the lines of the contour plots do not go over the label

#3 -> Number of spaces for formatting the label

#4 -> Offset to move the white spaces for the labels (so they are not centered)

My result using wxt terminal is:

The result is:

The code to generate the plot is:

reset
#Function map values on desired ranges. The values set to 90,95,99 are for later labelling
step90(x) = (x < 20.0  ? 0 :  (x < 50.0  ? 10 :  (x < 75.0  ? 20 : (x < 90.0  ? 80 : (x < 95.0  ? 90 : (x < 97.5 ? 95 :  (x < 99.0 ? 97 : (x < 99.5  ? 99 : (x < 99.9 ? 100 : 101 )))))))))
# enable 3D data read from a scattered data set in file
#71,46,10 are the number of different values for each axis X,Y,Z in "HeatMap_Test.txt" data file
#If values of dgrid3d are not set accordingly, weird contour values will be generated
set dgrid3d 71,46,10
set contour base # enable contour drawing
set view 0,0 # if you want to see the graph from above (2D plot)
unset surface # do not plot the surface, only the contour
set cntrparam levels discrete 90,95,99 #set contours only on values 90,95 and 99
set table "contours.dat" #Name of the output file to write the table
splot "HeatMap_Test.txt" u 2:1:(step90($3)) notitle            
unset table  #Write table to file

reset
#Map the Z values to desired ranges
step(x) = (x < 20.0  ? 0 :  (x < 50.0  ? 1 :  (x < 75.0  ? 2 : (x < 90.0  ? 3 : (x < 95.0  ? 4 : (x < 97.5 ? 5 :  (x < 99.0 ? 6 : (x < 99.5  ? 7 : (x < 99.9 ? 8 : 9 )))))))))

set palette maxcolors 10
set palette defined (0 "#333399", 1 "#3333f7", 2 "#3373ff", 3 "#33c6ff", 4 "#5affd2", 5 "#9cff8f", 6 "#dfff4c", 7 "#ffc733", 8 "#ff7a33", 9 "#e53333")

set cbrange [-0.5:9.5]
set cbtics nomirror
set cbtics ( ">99.9" 9, ">99.5" 8, ">99.0" 7, ">97.5" 6, ">95.0" 5, ">90.0" 4, ">75.0" 3, ">50.0" 2, ">20.0" 1, ">10.0" 0 )
set cblabel "%" norotate offset -8, 12

set xrange [-30:40]
set yrange [ 25:70]
set xtics 5
set ytics 5

set grid front linetype -1
set grid xtics lt 0 lw 1 lc rgb "#000000"
set grid ytics lt 0 lw 1 lc rgb "#000000"

load "<./draw_contourlines_label.sh contours.dat  0 4 0"
plot "HeatMap_Test.txt" u 2:1:(step($3)) with image, "world_10m.txt" notitle with lines ls 1 lc -1, "<./draw_contourlines_label.sh contours.dat 1 3 0" u 1:2 w l lw 2 lc 0 notitle

NOTE: If the file "contours.dat" is computed with GNUplot 4.6.4, the labels do not appear in the same way. Once the file is computed, with version 4.6+ show the same plot.

We can also use @maij approach using splot function. To do this, we first need to change the step function as he stated, because otherwise it produced weird lines. I renamed the function to step3D, but it is exactly @maij posted above:

step(x) = (x < 20.0 ? 0    : \
          (x < 50.0 ? 10   : \
          (x < 75.0 ? 20   : \
          (x < 90.0 ? 80   : \
          (x < 95.0 ? 89.999 : \
          (x < 97.5 ? 94.999 : \
          (x < 99.0 ? 97   : \
          (x < 99.5 ? 98.999 : \
          (x < 99.9 ? 100  : 101 )))))))))

With splot, there is also the need to format the input file into blocks. I did with this simple awk script, which can be put directly in the splot command:

awk 'NF>=1 && $1!~/#/ && $1!=prev {print \"\"} {prev=$1;print}' HeatMap_Test.txt

In GNUplot 5+, when plotting the colobar percentages appeared half-cutted, and the x and y labels were too separated, so I had to manually tweak them to fit the screen with this commands:

set rmargin at screen 0.8
set lmargin at screen 0.08
set tmargin at screen 0.95
set xtics offset 0, screen 0.036
set ytics offset screen 0.008, 0

The contour lines are plotted with the same command, so the result we get is similar (only the contour lines change a bit due to the ranges):

The code to generate the plot is:

reset
#Function map values on desired ranges. The values set to 90,95,99 are for later labelling
step3D(x) = (x < 20.0 ? 0    : (x < 50.0 ? 10   :  (x < 75.0 ? 20   : (x < 90.0 ? 80   : (x < 95.0 ? 89.999 : (x < 97.5 ? 94.999 : (x < 99.0 ? 97   : (x < 99.5 ? 98.999 :  (x < 99.9 ? 100  : 101 )))))))))
# enable 3D data read from a scattered data set in file
#71,46,10 are the number of different values for each axis X,Y,Z in "HeatMap_Test.txt" data file
#If values of dgrid3d are not set accordingly, weird contour values will be generated
set dgrid3d 71,46,10
set contour base # enable contour drawing
set view 0,0 # if you want to see the graph from above (2D plot)
unset surface # do not plot the surface, only the contour
set cntrparam levels discrete 90,95,99 #set contours only on values 90,95 and 99
set table "contours.dat" #Name of the output file to write the table
splot "HeatMap_Test.txt" u 2:1:(step3D($3)) notitle
unset table  #Write table to file


reset
#Map the Z values to desired ranges
step(x) = (x < 20.0  ? 0 :  (x < 50.0  ? 1 :  (x < 75.0  ? 2 : (x < 90.0  ? 3 : (x < 95.0  ? 4 : (x < 97.5 ? 5 :  (x < 99.0 ? 6 : (x < 99.5  ? 7 : (x < 99.9 ? 8 : 9 )))))))))

set palette maxcolors 10
set palette defined (0 "#333399", 1 "#3333f7", 2 "#3373ff", 3 "#33c6ff", 4 "#5affd2", 5 "#9cff8f", 6 "#dfff4c", 7 "#ffc733", 8 "#ff7a33", 9 "#e53333")

set cbrange [-0.5:9.5]
set cbtics nomirror
set cbtics ( ">99.9" 9, ">99.5" 8, ">99.0" 7, ">97.5" 6, ">95.0" 5, ">90.0" 4, ">75.0" 3, ">50.0" 2, ">20.0" 1, ">10.0" 0 )
set cblabel "%" norotate offset -8, 11.5

set xrange [-30:40]
set yrange [ 25:70]
set xtics 5
set ytics 5

set grid front linetype -1
set grid xtics lt 0 lw 1 lc rgb "#000000"
set grid ytics lt 0 lw 1 lc rgb "#000000"

set rmargin at screen 0.8
set lmargin at screen 0.08
set tmargin at screen 0.95
set xtics offset 0, screen 0.036
set ytics offset screen 0.008, 0
set pm3d explicit
set view map
load "<./draw_contourlines_label.sh contours.dat  0 4 0"
splot "< awk 'NF>=1 && $1!~/#/ && $1!=prev {print \"\"} {prev=$1;print}' HeatMap_Test.txt" u 2:1:(0):(step($3)) notitle w pm3d lc palette ,"world_10m.txt" u 1:2:(0) notitle with lines ls 1 lc rgb "#ffffff", "<./draw_contourlines_label.sh contours.dat 1 3 0" u 1:2:(0) w l lw 2 lc rgb "#000000" notitle

NOTE: If you are a Windows user, you can also do this plot using the Cygwin's GNUplot version



来源:https://stackoverflow.com/questions/40916407/contour-lines-with-labels-in-heatmap-with-gnuplot

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!