pythonmatplotlibcolorsrgbspectra

How can I fill the space between the x-axis and the curve with the spectral colors?


how can I fill the space between the x-axis and the spectral curve with the corresponding color? It only shows the colors in a band at the bottom. It would be great if the plot looks like the example: https://www.google.com/search?q=ocean+view+software&client=firefox-b-d&sxsrf=ALeKk00ddh8swDsz_7aKabw4-l1CXv3yEA:1593189268241&source=lnms&tbm=isch&sa=X&ved=2ahUKEwjByJLD9J_qAhUGwKQKHf7iBY4Q_AUoAXoECAwQAw&biw=1366&bih=654#imgrc=I3KeJE0Rq3taPM

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors
import pandas as pd

def wavelength_to_rgb(wavelength, gamma=0.8):
    wavelength = float(wavelength)
    if wavelength >= 380 and wavelength <= 750:
        A = 1.
    else:
        A=0.5
    if wavelength < 380:
        wavelength = 380.
    if wavelength >750:
        wavelength = 750.
    if wavelength >= 380 and wavelength <= 440:
        attenuation = 0.3 + 0.7 * (wavelength - 380) / (440 - 380)
        R = ((-(wavelength - 440) / (440 - 380)) * attenuation) ** gamma
        G = 0.0
        B = (1.0 * attenuation) ** gamma
    elif wavelength >= 440 and wavelength <= 490:
        R = 0.0
        G = ((wavelength - 440) / (490 - 440)) ** gamma
        B = 1.0
    elif wavelength >= 490 and wavelength <= 510:
        R = 0.0
        G = 1.0
        B = (-(wavelength - 510) / (510 - 490)) ** gamma
    elif wavelength >= 510 and wavelength <= 580:
        R = ((wavelength - 510) / (580 - 510)) ** gamma
        G = 1.0
        B = 0.0
    elif wavelength >= 580 and wavelength <= 645:
        R = 1.0
        G = (-(wavelength - 645) / (645 - 580)) ** gamma
        B = 0.0
    elif wavelength >= 645 and wavelength <= 750:
        attenuation = 0.3 + 0.7 * (750 - wavelength) / (750 - 645)
        R = (1.0 * attenuation) ** gamma
        G = 0.0
        B = 0.0
    else:
        R = 0.0
        G = 0.0
        B = 0.0
    return (R,G,B,A)

clim=(350,780)
norm = plt.Normalize(*clim)
wl = np.arange(clim[0],clim[1]+1,2)
colorlist = list(zip(norm(wl),[wavelength_to_rgb(w) for w in wl]))
spectralmap = matplotlib.colors.LinearSegmentedColormap.from_list("spectrum", colorlist)

fig, axs = plt.subplots(1, 1, figsize=(6,6), tight_layout=True)

# set directory
df = pd.read_excel('Reflexionsspektren.xlsx', 'Tabelle1')
# data for plot1
wavelengths = df['Wavelength']
spectrum = df['Fresh']

plt.plot(wavelengths, spectrum, color='darkred')

y = np.linspace(0, 6, 100)
X,Y = np.meshgrid(wavelengths, y)

extent=(np.min(wavelengths), np.max(wavelengths), np.min(y), np.max(y))

plt.imshow(X, clim=clim,  extent=extent, cmap=spectralmap, aspect='auto')
plt.xlabel('Wavelength (nm)')
plt.ylabel('Reflectance (%)')

plt.fill_between(wavelengths, spectrum, 8, color='w')
plt.savefig('WavelengthColors.png', dpi=600)

plt.show()

Solution

  • Unfortunately I could not run your code because I dont have the 'Reflexionsspektren.xlsx' file. However, a hacky solution based on this post gets the desired result I believe. I used np.vectorize to convert your wavelength_to_rgb function into an array of colors for each wavelength value on the x-axis. Using matplotlib's fill_between the spectrum is drawn between the curve and the x-axis.

    import matplotlib.pyplot as plt
    import numpy as np
    
    def wavelength_to_rgb(wavelength, gamma=0.8):
        wavelength = float(wavelength)
        if wavelength >= 380 and wavelength <= 750:
            A = 1.
        else:
            A=0.5
        if wavelength < 380:
            wavelength = 380.
        if wavelength >750:
            wavelength = 750.
        if wavelength >= 380 and wavelength <= 440:
            attenuation = 0.3 + 0.7 * (wavelength - 380) / (440 - 380)
            R = ((-(wavelength - 440) / (440 - 380)) * attenuation) ** gamma
            G = 0.0
            B = (1.0 * attenuation) ** gamma
        elif wavelength >= 440 and wavelength <= 490:
            R = 0.0
            G = ((wavelength - 440) / (490 - 440)) ** gamma
            B = 1.0
        elif wavelength >= 490 and wavelength <= 510:
            R = 0.0
            G = 1.0
            B = (-(wavelength - 510) / (510 - 490)) ** gamma
        elif wavelength >= 510 and wavelength <= 580:
            R = ((wavelength - 510) / (580 - 510)) ** gamma
            G = 1.0
            B = 0.0
        elif wavelength >= 580 and wavelength <= 645:
            R = 1.0
            G = (-(wavelength - 645) / (645 - 580)) ** gamma
            B = 0.0
        elif wavelength >= 645 and wavelength <= 750:
            attenuation = 0.3 + 0.7 * (750 - wavelength) / (750 - 645)
            R = (1.0 * attenuation) ** gamma
            G = 0.0
            B = 0.0
        else:
            R = 0.0
            G = 0.0
            B = 0.0
        return (R,G,B,A)
    
    if __name__ == '__main__':
        x = np.linspace(350, 780, 430)
        y = np.sin(x / 100)  # replace with your function / measured values.
        colors = np.array(np.vectorize(wavelength_to_rgb)(x))
        fig, ax = plt.subplots()
        ax.plot(x, y, color="gray")
        for i in range(len(x) - 1):
            plt.fill_between([x[i], x[i+1]], [y[i], y[i+1]], color=colors[:, i])
    

    enter image description here