Some what related to previous question
I would like to take the default (pm3d default) colour palette of gnuplot and place a white value at X and have anything >=X as white but the rest(<X) still evently distributed with the default value.
Say for example I have values between 0 and 100. I am only interested in values 0 to 30 so I do the following:
set cbrange [0:30]
Now values are evenly distributed between 0 and 30 with the default colour palette, however values 30.001 to 100 are all yellow. I would like to place a white block at the top of my colour palette, say something like this on the colour bar
0:30 evenly distribute with default palette
30:31 white
and in the actual plot, have values >=30 as white.
I know I can set defined values, but I can't seem to combine the default rgbformula 7,5,15 and a defined point of 30=white.
Any thoughts?
You can define your own, function-based palette with set palette functions. Typing show palette rgbformulae shows you the definitions of the functions used for the default palette (7,5,15):
gnuplot> show palette rgbformulae
* there are 37 available rgb color mapping formulae:
0: 0 1: 0.5 2: 1
3: x 4: x^2 5: x^3
6: x^4 7: sqrt(x) 8: sqrt(sqrt(x))
9: sin(90x) 10: cos(90x) 11: |x-0.5|
12: (2x-1)^2 13: sin(180x) 14: |cos(180x)|
15: sin(360x) 16: cos(360x) 17: |sin(360x)|
...
So you can define your own functions for red, green and blue which give white at one end of the palette:
r(x) = sqrt(x)
g(x) = x**3
b(x) = (x == 1 ? 1 : sin(2*pi*x))
set palette functions r(gray),g(gray),b(gray)
For demonstration, here is a full example script, where all values above -10 are white:
r(x) = sqrt(x)
g(x) = x**3
b(x) = (x == 1 ? 1 : sin(2*pi*x))
set palette functions r(gray),g(gray),b(gray)
set isosamples 100
set pm3d map
set cbrange [-200:-10]
set cbtics -200,40
set cbtics add ('> -10' -10)
splot -x**2 - y**2 notitle
Output with 4.6.5 is:
Related
I use the splot commadn to produce a heat map of the earth. The x- and y-values represent lattitude and longitude of a specific point on the Earth's surface, while the related z-value is the outcome of an analysis. The zrange is between 0 and 60. However, for some locations on Earth, there is no result available (which is correct) and z is set to 9999 for these cases.
I'm using the following script to produce the heat map:
set terminal png large size 1600,\
1200 font arial 24 crop
set output "map.png"
set palette model RGB defined (0 "dark-green",1 "forest-green",2 "green",3 "light-green",4 "dark-yellow",5 "yellow",6 "red",7 "dark-red")
set xrange[-180.00: 180.00]
set yrange[ -90.00: 90.00]
set zrange[ *: 60]
set grid
set pm3d map
set xlabel "Longitude [deg]"
set ylabel "Latitude [deg]"
unset key
set cblabel "Time [h]"
splot "output\\map.dat" u 5:6:8,\
"input\\world.dat" u 1:2:( .00) w l lw 1 lt -1
It works fine but because of the limitation in zrange, regions with z > 60 are shown in white.
I want to have something like a condition which enables that all 9999 z-values are shown in a specific colour like purple with a declaration like "no result" in the legend.
Any idea how to achieve this?
Thanks in advance,
Florian
Not exactly sure how to modify the style for the selected points, but you can use the ternary operator not to draw them at all. Something like:
splot "output\\map.dat" u 5:6:(($8<=60)?($8):(1/0))
You basically want to have 3 "ranges" of colors:
0 to 60 your defined palette colors
>60 "out of range" color
=9999 "no data" color
Not sure if splot ... w pm3d will allow an easy "independent" setting for z and color.
Furthermore, if you have NxN datapoints you will get (N-1)x(N-1) quadrangles and the color is determined by the z-values of the involved vertices (check help corners2color) and http://gnuplot.sourceforge.net/demo_5.5/pm3d.html (the very last graph). Maybe there is an easy way which I am not aware of.
That's why I would perfer the plotting style with boxxyerror (check help boxxyerror), maybe this is not the intended way, but it is rather flexible. If you are running gnuplt 5.4 you have the function palette() (check help palette).
I would take for missing data (backgroundcolor here:white) and for data out of range "grey", but you can easily change it. You can skip the random data generation part and in the plot command replace $Data with your filename and the corresponding columns. As well, replace 180./N and 90./N with the width (delta longitude) and height (delta latitude) of one data element.
Script: (requires gnuplot>=5.4)
### define separate color for missing values
reset session
set xrange[-180:180]
set yrange[-90:90]
# create some "random" test data
N = 90
set samples N
set isosamples N
set table $Data
c = 0.05
x0 = 70 # (rand(0)*360-180) # or random
y0 = -50 # (rand(0)*180-90) #
size0 = 2
x1 = -150 # (rand(0)*360-180) # or random
y1 = -20 # (rand(0)*180-90) #
size1 = 1
holeP0(x,y) = (1-erf((x-x0)*c/size0)**2) * (1-erf((y-y0)*c/size0)**2)
holeP1(x,y) = (1-erf((x-x1)*c/size1)**2) * (1-erf((y-y1)*c/size1)**2)
f(x,y) = rand(0)<holeP0(x,y) || rand(0)<holeP1(x,y) ? 9999 : (sin(1.3*x*c)*cos(.9*y*c)+cos(.8*x*c)*sin(1.9*y*c)+cos(y*.2*x*c**2))*11.5+33
splot f(x,y)
unset table
set palette model RGB defined (0 "dark-green",1 "forest-green",2 "green",3 "light-green",4 "dark-yellow",5 "yellow",6 "red",7 "dark-red")
myZmin = 0
myZmax = 60
myColorNoData = 0xffffff
myColorOutOfRange = 0x999999
set rmargin screen 0.8
set colorbox user origin screen 0.85,graph 0.2 size graph 0.05,graph 0.8
set cblabel "Amplitude"
set cbrange [myZmin:myZmax]
set tics out
set style fill solid 1.0 border
set key noautotitle at graph 1.27, graph 0.15 reverse Left samplen 2
myColor(col) = (z=column(col), z==9999 ? myColorNoData : z>myZmax ? myColorOutOfRange : palette(z))
plot $Data u 1:2:(180./N):(90./N):(myColor(3)) w boxxy lc rgb var, \
"world.dat" u 1:2:(0) w l lc "black", \
NaN w l lc palette, \
keyentry w boxes lc rgb 0x000000 fill empty ti "no data", \
keyentry w boxes lc rgb myColorOutOfRange ti "\ndata out\nof range"
### end of script
Result:
With palette it is easy to create color gradients
set view map
set samp 50,50
set palette defined (0 "blue", 1 "green", 2 "red")
spl "++" us 1:2:1 palette pt 5
Now I would like to apply transparency in vertical direction. The option lc rbg variable supports transparency via the alpha channel (see also here):
spl "++" us 1:2:1:(int(($2+5)/10*255)<<24) lc rgb var pt 5
But how can I translate the palette colors into rgb colors?
A second question: why I get only 10 horizontal rows, albeit I specified 50 in samp?
Easy answer first: When there is 2-dimensional sampling, either automatically from splot or explicitly from plot '++', the number of samples in the first dimension is controlled by set sample and the number of samples in the second dimension is controlled by set isosample.
Now the harder one. In gnuplot versions through the current 5.2.8 you cannot add transparency directly to the palette. You can, however, go through a multi-step process of saving the palette into a file or datablock and then reading it back it as an array of RGB colors. Once you have that array you can add an alpha channel value so that it expresses transparency as well. I will show this process using the datablock created by the command test palette. In older versions of gnuplot you may have to instead use the file created by set print "palette.save"; show palette palette 256;.
# save current palette to a datablock as a list of 256 RGB colors, one per line
set palette defined (0 "blue", 1 "green", 2 "red")
test palette
# print one line to show the format (cbval R G B NTSCval)
print $PALETTE[4]
# Create an array of packed RGB values
array RGB[256]
do for [i=1:256] {
Red = int(255. * word($PALETTE[i],2))
Green = int(255. * word($PALETTE[i],3))
Blue = int(255. * word($PALETTE[i],4))
RGB[i] = Red << 16 | Green << 8 | Blue
}
# Sample from '++' are generated to span ranges on the u and v axes
# I choose 1:256 so that the y coordinates match the range of array indices
set sample 50
set isosample 50
set urange [1:256]
set vrange [1:256]
set xrange [*:*] noextend
set yrange [*:*] noextend
# Now you can use colors stored in the array via colorspec `rgb variable`
# which will also accept an alpha channel in the high bits
plot "++" using 1:2:(RGB[int($2)]) with points pt 5 lc rgb variable
# The final step is to add an alpha channel as a function of y
# Here I go from opaque (Alpha = 0) to 50% transparent (Alpha = 127)
# This works because I know y will run from 1-256
ARGB(y) = RGB[int(y)] + (int(y/2)<<24)
plot "++" using 1:2:(ARGB($2)) with points pt 5 lc rgb variable
Output shown below.
The required command sequence, as you can see, is a mess.
It will be much easier in the next gnuplot release (5.4). The new version will provide a function palette(z) that converts from the current palette directly to a packed RGB value. Note that the palette() function isn't in the -rc1 testing version but will be in -rc2. So in version 5.4 all that palette/array/RGB manipulation can be replaced by
plot '++' using 1:2:(palette($2) + (int($2)<<24)) with points pt 5 lc rgb variable
Check also this: Gnuplot: transparency of data points when using palette
First of all, you can check what your defined palette is doing:
set palette defined (0 "blue", 1 "green", 2 "red")
test palette
You will get this:
Each channel (R,G,B) has a function with an input range [0:1] and an output range [0:1]. In this case it is a linear gradient.
So, you have to define such a function and put the channels together with the transparency (alpha) channel using the bit shift (see help operators binary).
The nice thing about a palette is that gnuplot takes care about the range. Here, you have to know minimum and maximum in advance and scale the color accordingly. You could use stats for this.
Code:
### your own palette with transparency
reset session
r(x) = x < 0.5 ? 0 : 2*x -1
g(x) = x < 0.5 ? 2*x : 2-2*x
b(x) = x < 0.5 ? 1-2*x : 0
a(y) = y
myColor(x,y) = (int(a((y-yMin)/(yMax-yMin))*0xff)<<24) + \
(int(r((x-xMin)/(xMax-xMin))*0xff)<<16) + \
(int(g((x-xMin)/(xMax-xMin))*0xff)<<8) + \
int(b((x-xMin)/(xMax-xMin))*0xff)
set samples 50
set isosamples 50
set size square
xMin=-5; xMax=5
yMin=-5; yMax=5
plot '++' u 1:2::(myColor($1,$2)) w p pt 5 ps 0.5 lc rgb var notitle
### end of code
Result:
I would like to draw an arrow with not a single colour, but a colour gradient along its length. Does anyone know how to achieve that? Some pseudo-code for creating an arrow that starts red and ends blue:
set palette defined (0 "red", 1 "blue")
set cbr [0:1]
set arrow from 0,0 to 1,1 linecolor palette cb [0:1] # this doesn't work
Besides #Friedrich's solution, I would like to suggest a more general solution (although more complicated).
I assume you want to plot something else besides the arrow.
In case your graph needs to use a palette I guess you're in "trouble", because I'm not sure whether gnuplot supports more than one palette in a plot command
(see Gnuplot 5.2 splot: Multiple pm3d palette in one plot call). So, you have to implement the palette for your arrow by yourself (see e.g. Gnuplot: transparency of data points when using palette). If you want to do bent arrows using Cubic Bézier check (https://stackoverflow.com/a/60389081/7295599).
Code:
### arrow with color gradient (besides other palette in plot)
reset session
array A[4] = [-4,-2,4,2] # arrow coordinates x0,y0,x1,y1
Ax(t) = A[1] + t*(A[3]-A[1])
Ay(t) = A[2] + t*(A[4]-A[2])
AColorStart = 0xff0000 # red
AColorEnd = 0x0000ff # blue
r(c) = (c & 0xff0000)>>16
g(c) = (c & 0x00ff00)>>8
b(c) = (c & 0x0000ff)
AColor(t) = ((int(r(AColorStart)*(1-t)+r(AColorEnd)*t))<<16) + \
((int(g(AColorStart)*(1-t)+g(AColorEnd)*t))<<8) + \
int(b(AColorStart)*(1-t)+b(AColorEnd)*t)
array AHead[1] # dummy array for plotting a single point, here: arrow head
set angle degrees
set style arrow 1 lw 3 lc rgb var size 0.5,15 fixed
set palette grey
plot '++' u 1:2:($1*$2) w image notitle, \
[0:0.99] '+' u (Ax($1)):(Ay($1)):(AColor($1)) w l lw 3 lc rgb var notitle,\
AHead u (Ax(0.99)):(Ay(0.99)):(Ax(1)-Ax(0.99)):(Ay(1)-Ay(0.99)):(AColor($1)) w vec as 1 notitle
### end of code
Result:
Addition:
For what it is worth, here is a variation which allows plotting of multiple arrows each with a different palette. I guess it requires gnuplot 5.2, because of indexing the datablock $PALETTE[i].
Code:
### multiple arrows each with different color gradients (besides other palette in plot)
reset session
# define palettes
set print $myPalettes
test palette # get default palette into datablock $PALETTE
print $PALETTE # add palette to $myPalettes
set palette rgb 33,13,10 # define next palette
test palette # get palette into datablock $PALETTE
print $PALETTE # add palette to $myPalettes
set palette defined (0 "blue", 1 "black", 2 "red") # define next palette
test palette # get palette into datablock $PALETTE
print $PALETTE # add palette to $myPalettes
set print
ColorComp(p,t,c) = int(word($myPalettes[p*257+int(255*t)+1],c+1)*0xff)
AColor(p,t) = (ColorComp(p,t,1)<<16) + (ColorComp(p,t,2)<<8) + ColorComp(p,t,3)
set size ratio -1
set angle degrees
unset key
set style arrow 1 lw 3 lc rgb var size 0.5,15 fixed
array AHead[1] # dummy array for plotting a single point, here: arrow head
set palette grey # yet another palette for the background
# x0 y0 x1 y1 paletteNo
$Arrows <<EOD
-4 -4 4 0 0
-4 -2 4 2 1
-4 0 4 4 2
EOD
Ax(i,t) = word($Arrows[i],1) + t*(word($Arrows[i],3)-word($Arrows[i],1))
Ay(i,t) = word($Arrows[i],2) + t*(word($Arrows[i],4)-word($Arrows[i],2))
Palette(i) = int(word($Arrows[i],5))
plot '++' u 1:2:($1*$2) w image, \
for [i=1:|$Arrows|] [0:0.99:0.01] '+' u (Ax(i,$1)):(Ay(i,$1)):(AColor(Palette(i),$1)) w l lw 4 lc rgb var, \
for [i=1:|$Arrows|] AHead u (Ax(i,0.99)):(Ay(i,0.99)): \
(Ax(i,1)-Ax(i,0.99)):(Ay(i,1)-Ay(i,0.99)):(AColor(Palette(i),$1)) w vec as 1
### end of code
Result:
With line palette one can color-code a line. With a second command one could set the head, via set arrow or with a plot vector command
set palette defined (0 "red", 1 "blue")
set cbr [0:1]
set arrow 1 from 0.9,0.9 to 1,1 lc "blue"
plot sample [t=0:1] "+" us (t):(t):(t) w l palette
Thus two commands are necessary. The head of the arrow has single colour, which you have to specify.
I am currently plotting some data using gnuplot with rowstacked histograms.
The problem is, that the colors start to repeat after 9 different colors have been chosen. One can see this also happening in the official gnuplot examples (see http://gnuplot.sourceforge.net/demo/histograms.html - Example 4 & 5)
Is there any way to tell gnuplot to use more different colors?
There is no fully automated way to do this, but you can define as many line styles as you want with set style line ... and then use them. Here, I just use a simple iteration to define several colors:
do for [i=1:20] {
set style line i linecolor rgb hsv2rgb(0.05*(i-1), 1, 1)
}
set style data histograms
set style histogram rowstacked
set style fill solid border -1
set boxwidth 0.75
plot for [i=1:20] 'mydata.dat' u 2 ls i t 'ls '.i
The data file contains just the values
1 0.1
2 0.2
3 0.3
Note, that the hsv2rgb function is defined only since 5.0. For earlier version you can use the following user-defined function to get the same functionality:
rgb2int(r,g,b) = int(255*r)*2**16 + int(255*g)*2**8 + int(255*b)
hsv2rgb(h,s,v) = (s == 0 ? rgb2int(v,v,v) : (HSV_h = h*6.0, HSV_i = int(floor(HSV_h)), HSV_f = HSV_h - HSV_i, HSV_p = v*(1.0 - s), HSV_q = v*(1.0-s*HSV_f), HSV_t = v*(1.0-s*(1.0-HSV_f)), (HSV_i%6 == 0 ? rgb2int(v,HSV_t,HSV_p) : (HSV_i%6 == 1 ? rgb2int(HSV_q,v,HSV_p) : (HSV_i%6 == 2 ? rgb2int(HSV_p,v,HSV_t) : (HSV_i%6 == 3 ? rgb2int(HSV_p,HSV_q,v) : (HSV_i%6 == 4 ? rgb2int(HSV_t,HSV_p,v) : rgb2int(v,HSV_p,HSV_q))))))))
To make it easier, you could put this code into a configuration file, or a third script hsv2rgb.gp and include it with load 'hsv2rgb.gp' before using the function.
Output with 4.6.3 is:
There is something a little more automated than Christoph's answer. You can use a color palette:
set palette rgb 7,5,15
unset colorbox
plot 'immigration.dat' using 2:xtic(1) title columnheader(2), \
for [i=3:22] '' using i lt palette frac i/22. title columnheader(i)
The trick is, to define frac based on the loop counter i. Be sure to devide by a float (in this case 22. to match the example) to get the right fractions of the color palette.
Or, for example 4:
plot 'immigration.dat' using (100.*$2/$24):xtic(1) t column(2), \
for [i=3:23] '' using (100.*column(i)/column(24)) lt palette frac i/23.\
title column(i)
Now, you only have to decide on a suitable color palette.
I am trying to set textcolor property of a label in gnuplot to transition through a palette of colours.
To be more precise, I want each letter of the label, say "Number of Connections", to be a different color but following the color palette I specify.
I tried using the following method, but it failed, using only the color in the middle of the range for the string.
set palette model RGB defined ( \
0 '#F46D43',\
1 '#FDAE61',\
2 '#FEE08B',\
3 '#E6F598',\
4 '#ABDDA4',\
5 '#66C2A5' )
set y2label "Number of Connections" textcolor palette
Unfortunately, gnuplot can only color the entire string "Number of Connections". You can influence the color using the additional frac option.
However, here's a way to achieve what you were looking for. It involves some manual settings, though, as I'll explain below:
# define the location of your plot:
bm = 0.15
lm = 0.12
rm = 0.75
tm = 0.90
# letter spacing - play with this as needed:
STRDIST = 0.03
# set up the plot window:
set lmargin at screen lm
set rmargin at screen rm
set bmargin at screen bm
set tmargin at screen tm
# place the colorbar in a defined location:
set colorbox vertical user origin rm+0.1,0.15 size .05,tm-bm
# define your palette:
set palette model RGB defined ( \
0 '#F46D43',\
1 '#FDAE61',\
2 '#FEE08B',\
3 '#E6F598',\
4 '#ABDDA4',\
5 '#66C2A5' )
# your label
LABEL = "Number of Connections"
# the 'length' of LABEL, unfortunately counted manually:
LEN_LABEL = 21.0 # IMPORTANT, declare as float
# use a loop to individually place each char of the string on the plot:
do for [i=1:LEN_LABEL]{\
set label i LABEL[i:i] at screen 0.8,bm+((i-1.)*STRDIST) \
rotate by 90 textcolor palette frac i/LEN_LABEL\
}
# dummy function plot (so that there's something to see):
plot '+' using ($1):(sin($1)):(0.5*(1.0+sin($1))) w l lw 3 lc pal not
What is going on:
Define the location of your plot and of the colorbar: That way you will know exactly where they are and can place a "pseudo"-label accurately.
The variable STRDIST is used to space the individual letters. This is clumsy, but you get the gist. Play with it to achieve good results.
Unfortunately, it seems that gnuplot cannot compute the length of a string, so I hard-wired it, LEN_LABEL.
Use a do for-loop to place each letter of the label string on the plot, assigning a color from the color palette using the additional frac option. frac 0.0 is the lowest and frac 1.0 the "highest" color on the color palette. Here, we exploit the loop-counter to give evenly spaced colors from the palette. Note: This is why it is important to declare LEN_LABEL as a float, not integer or everything but the last iteration will result in frac 0.
The plot '+' ... command is borrowed from this site.
The plot you get when you copy/paste the above example looks like this:
Play with the starting point of the "label" as well as the STRDIST to generate/place a label of your liking.