Search code examples
gnuplotscale

How to get the relationship between X and Y ranges in gnuplot


I am plotting data using circles like this:

$DATA<<EOD
1.0000,"CAN",80.6600,1.6700,0.2879,
2.0000,"DEU",79.8400,1.3600,0.4134,
3.0000,"DNK",78.6000,1.8400,0.2144,
4.0000,"EGY",72.7300,2.7800,0.4077,
5.0000,"GBR",80.0500,2.0000,0.3610,
6.0000,"IRN",72.4900,1.7000,0.3906,
7.0000,"IRQ",68.0900,4.7700,0.2810,
8.0000,"ISR",81.5500,2.9600,0.2195,
9.0000,"RUS",68.6000,1.5400,0.5696,
10.0000,"USA",78.0000,2.0000,1.0000,
EOD
set datafile separator comma
set style data circles
set term windows
unset xtics; unset ytics; unset x2tics; unset y2tics
set grid xtics layerdefault
set grid ytics layerdefault
set xtics nomirror
set ytics nomirror
set border 3.00000
set colorsequence classic
set title "This is My Title"
set xlabel "Cities"
set ylabel "Population (in Millions)"
set key default
set style fill solid 4.00 transparent
plot $DATA every ::0::0 using ($3):($4):($5) title "CAN",\
$DATA every ::1::1 using ($3):($4):($5) title "DEU",\
$DATA every ::2::2 using ($3):($4):($5) title "DNK",\
$DATA every ::3::3 using ($3):($4):($5) title "EGY",\
$DATA every ::4::4 using ($3):($4):($5) title "GBR",\
$DATA every ::5::5 using ($3):($4):($5) title "IRN",\
$DATA every ::6::6 using ($3):($4):($5) title "IRQ",\
$DATA every ::7::7 using ($3):($4):($5) title "ISR",\
$DATA every ::8::8 using ($3):($4):($5) title "RUS",\
$DATA every ::9::9 using ($3):($4):($5) title "USA",\
$DATA every ::9::9 using ($3):($4)+($5):($2) with labels center notitle,\
$DATA every ::9::9 using ($3)-($5):($4):($2) with labels center notitle,\
$DATA every ::9::9 using ($3):($4)-($5)/2+0.1:($2) with labels center notitle

Output here

With my last three lines I am trying to position a label above, left and below the circle.

Positioning the label on the left (or right) of the circle is not a problem since the radius of the circle matches the scale for the x plane. This is ok.

Positioning the label at top of the circle does not work the same because the y scale is not the same as the x plane.

Positioning the label at bottom of the circle can be done but it requires some calculation, the added logic to divide by 2 and add 0.1 works for the USA entry but it fails for smaller data points.

My question is: Is there a way to get the relationship\scale between the X and Y planes so I can calculate the offset for the Top\Bottom labels? (Besides issuing a show x\yrange commands and reading back the output)

I tried the "set view equal xy" command but it changed nothing, and the "show size" command shows 1,1 for the current scale.

Thank you Roberto


Solution

  • You can access variables named GPVAL_X_MIN and others. So you can use:

    charsize=1.5
    LEN=3
    ratio=1
    
    plot '012_d.dat' every ::9::9 using 3:4:5 w circles title "USA",\
    '' every ::9::9 using 3:($4+ratio*$5):2 with labels center offset 0,charsize notitle,\
    '' every ::9::9 using ($3-$5):4:2 with labels center offset -LEN,0 notitle,\
    '' every ::9::9 using 3:($4-ratio*$5):2 with labels center offset 0,-charsize notitle
    
    ratio=(GPVAL_Y_MAX-GPVAL_Y_MIN)/(GPVAL_X_MAX-GPVAL_X_MIN)
    
    replot
    

    Solution

    The key point (and backward) of the solution is that GPVAL_X_MIN (and others) has a correct value after a plot, therefore you have to use replot.

    NOTES

    1. If you don't transform the value of a column, you don't have to use "$" eg. using ($3):($4):($5) is equal to using 3:4:5

    2. If you use the data(file) again in a plot command, you don't have to write the (file)name again: ''.

    EDIT

    1. As you said, you can (have to) fine tuning the solution: in the using 3:($4+ratio*$5):2 you can use a constant multiplier; and you can change (decrease) the charsize. In this case, the offset of the labels will be more dependent on the data.

    2. As @theozh said, you can use a loop, to compress your code. However, it is more tricky as I thought at first:

    At first think, you have to use something like:

    plot for[i=0:9] '012_d.dat' every ::i::i using 3:4:5 title $2
    

    However, this do not make any key. My second though was:

    plot for[i=0:9] '012_d.dat' every ::i::i using 3:4:5 title sprintf("%s",$2)
             f_sprintf: attempt to print numeric value with string format
    

    As you can see, that isn't better. After a search for help plot title

    Syntax: title < text> | notitle [< ignored text>] title columnheader | title columnheader(N) {at {beginning|end}} {{no}enhanced}

    where < text> is a quoted string or an expression that evaluates to a string. The quotes will not be shown in the key. Note: Starting with gnuplot version 5.4, if < text> is an expression or function it it evaluated after the corresponding function or data stream is plotted. This allows the title to reference quantities calculated or input during plotting, which was not possible in earlier gnuplot versions.

    I have figured out a fake assignment in the using part:

    plot for[i=0:9] '012_d.dat' every ::i::i using ((x=stringcolumn(2)) eq "XXX"?1/0:$3):4:5 title sprintf("%s",x)
    

    which has worked :) With the other parts of the plot:

    plot for[i=0:9] '012_d.dat' every ::i::i using ((x=stringcolumn(2)) eq "XXX"?1/0:$3):4:5 title sprintf("%s",x),\
     for[i=0:9] '' every ::i::i using 3:($4+ratio*$5):2 with labels center offset 0,charsize notitle,\
     for[i=0:9] '' every ::i::i using ($3-$5):4:2 with labels center offset -LEN,0 notitle,\
     for[i=0:9] '' every ::i::i using 3:($4-ratio*$5):2 with labels center offset 0,-charsize notitle
    

    with loop