Improve customizability of Landsat band plots (#1378)

* Improve customizability of Landsat band plots

* Better xlabel

* Display default values

* More band customization

* More band customization

* Fix ylim

* Fix vertical centering

* Fewer sensors

* flake8 fix

* usetex

* Shorter, simpler

* Wider thermal bands padding
This commit is contained in:
Adam J. Stewart 2023-06-01 15:12:04 -05:00 коммит произвёл GitHub
Родитель 02f2a6cb81
Коммит 9e4f7d12d4
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
2 изменённых файлов: 146 добавлений и 85 удалений

Просмотреть файл

@ -1,31 +1,50 @@
Satellite name,Sensor name,Band number,Wavelength start (μm),Wavelength width,Color,Resolution (m)
Landsat 4-5,tm,6,10.4,2.1,#8c564b,120
Landsat 7,etm,6,10.4,2.1,#8c564b,60
Landsat 8-9,tirs,10,10.6,0.59,#8c564b,100
Landsat 8-9,tirs,11,11.5,1.01,#8c564b,100
Landsat 1-5,mss,1,0.5,0.1,#2ca02c,60
Landsat 1-5,mss,2,0.6,0.1,#d62728,60
Landsat 1-5,mss,3,0.7,0.1,#e377c2,60
Landsat 1-5,mss,4,0.8,0.3,#ff7f0e,60
Landsat 4-5,tm,1,0.45,0.07,#1f77b4,30
Landsat 4-5,tm,2,0.52,0.08,#2ca02c,30
Landsat 4-5,tm,3,0.63,0.06,#d62728,30
Landsat 4-5,tm,4,0.76,0.14,#e377c2,30
Landsat 4-5,tm,5,1.55,0.2,#ff7f0e,30
Landsat 4-5,tm,7,2.08,0.27,#7f7f7f,30
Landsat 7,etm,1,0.45,0.07,#1f77b4,30
Landsat 7,etm,2,0.52,0.08,#2ca02c,30
Landsat 7,etm,3,0.63,0.06,#d62728,30
Landsat 7,etm,4,0.77,0.13,#e377c2,30
Landsat 7,etm,5,1.55,0.2,#ff7f0e,30
Landsat 7,etm,7,2.09,0.26,#7f7f7f,30
Landsat 7,etm,8,0.52,0.38,#bcbd22,15
Landsat 8-9,oli,1,0.43,0.02,#17becf,30
Landsat 8-9,oli,2,0.45,0.06,#1f77b4,30
Landsat 8-9,oli,3,0.53,0.06,#2ca02c,30
Landsat 8-9,oli,4,0.64,0.03,#d62728,30
Landsat 8-9,oli,5,0.85,0.03,#e377c2,30
Landsat 8-9,oli,6,1.57,0.08,#ff7f0e,30
Landsat 8-9,oli,7,2.11,0.18,#7f7f7f,30
Landsat 8-9,oli,9,1.36,0.02,#9467bd,30
Landsat 8-9,oli,8,0.5,0.18,#bcbd22,15
Satellite,Sensor,Band,Wavelength Start (μm),Wavelength Width,Color,Resolution (m)
Landsat 1--2,RBV,1,0.475,0.1,#1f77b4,80
Landsat 1--2,RBV,2,0.58,0.1,#2ca02c,80
Landsat 1--2,RBV,3,0.69,0.14,#d62728,80
Landsat 3,RBV,1,0.505,0.245,#bcbd22,40
Landsat 1--5,MSS,1,0.5,0.1,#2ca02c,80
Landsat 1--5,MSS,2,0.6,0.1,#d62728,80
Landsat 1--5,MSS,3,0.7,0.1,#e377c2,80
Landsat 1--5,MSS,4,0.8,0.3,#ff7f0e,80
Landsat 4--5,TM,1,0.45,0.07,#1f77b4,30
Landsat 4--5,TM,2,0.52,0.08,#2ca02c,30
Landsat 4--5,TM,3,0.63,0.06,#d62728,30
Landsat 4--5,TM,4,0.76,0.14,#e377c2,30
Landsat 4--5,TM,5,1.55,0.2,#ff7f0e,30
Landsat 4--5,TM,6,10.4,2.1,#8c564b,120
Landsat 4--5,TM,7,2.08,0.27,#7f7f7f,30
Landsat 6,ETM,1,0.45,0.07,#1f77b4,30
Landsat 6,ETM,2,0.52,0.08,#2ca02c,30
Landsat 6,ETM,3,0.63,0.06,#d62728,30
Landsat 6,ETM,4,0.76,0.14,#e377c2,30
Landsat 6,ETM,5,1.55,0.2,#ff7f0e,30
Landsat 6,ETM,6,10.4,2.1,#8c564b,120
Landsat 6,ETM,7,2.08,0.27,#7f7f7f,30
Landsat 6,ETM,8,0.52,0.38,#bcbd22,15
Landsat 7,ETM+,1,0.45,0.07,#1f77b4,30
Landsat 7,ETM+,2,0.52,0.08,#2ca02c,30
Landsat 7,ETM+,3,0.63,0.06,#d62728,30
Landsat 7,ETM+,4,0.77,0.13,#e377c2,30
Landsat 7,ETM+,5,1.55,0.2,#ff7f0e,30
Landsat 7,ETM+,6,10.4,2.1,#8c564b,60
Landsat 7,ETM+,7,2.08,0.27,#7f7f7f,30
Landsat 7,ETM+,8,0.52,0.38,#bcbd22,15
Landsat 8--9,OLI/TIRS,1,0.43,0.02,#17becf,30
Landsat 8--9,OLI/TIRS,2,0.45,0.06,#1f77b4,30
Landsat 8--9,OLI/TIRS,3,0.53,0.06,#2ca02c,30
Landsat 8--9,OLI/TIRS,4,0.64,0.03,#d62728,30
Landsat 8--9,OLI/TIRS,5,0.85,0.03,#e377c2,30
Landsat 8--9,OLI/TIRS,6,1.57,0.08,#ff7f0e,30
Landsat 8--9,OLI/TIRS,7,2.11,0.18,#7f7f7f,30
Landsat 8--9,OLI/TIRS,8,0.5,0.18,#bcbd22,15
Landsat 8--9,OLI/TIRS,9,1.36,0.02,#9467bd,30
Landsat 8--9,OLI/TIRS,10,10.6,0.59,#8c564b,100
Landsat 8--9,OLI/TIRS,11,11.5,1.01,#8c564b,100

1 Satellite name Satellite Sensor name Sensor Band number Band Wavelength start (μm) Wavelength Start (μm) Wavelength width Wavelength Width Color Resolution (m)
2 Landsat 4-5 Landsat 1--2 tm RBV 6 1 10.4 0.475 2.1 0.1 #8c564b #1f77b4 120 80
3 Landsat 7 Landsat 1--2 etm RBV 6 2 10.4 0.58 2.1 0.1 #8c564b #2ca02c 60 80
4 Landsat 8-9 Landsat 1--2 tirs RBV 10 3 10.6 0.69 0.59 0.14 #8c564b #d62728 100 80
5 Landsat 8-9 Landsat 3 tirs RBV 11 1 11.5 0.505 1.01 0.245 #8c564b #bcbd22 100 40
6 Landsat 1-5 Landsat 1--5 mss MSS 1 1 0.5 0.5 0.1 0.1 #2ca02c 60 80
7 Landsat 1-5 Landsat 1--5 mss MSS 2 2 0.6 0.6 0.1 0.1 #d62728 60 80
8 Landsat 1-5 Landsat 1--5 mss MSS 3 3 0.7 0.7 0.1 0.1 #e377c2 60 80
9 Landsat 1-5 Landsat 1--5 mss MSS 4 4 0.8 0.8 0.3 0.3 #ff7f0e 60 80
10 Landsat 4-5 Landsat 4--5 tm TM 1 1 0.45 0.45 0.07 0.07 #1f77b4 30
11 Landsat 4-5 Landsat 4--5 tm TM 2 2 0.52 0.52 0.08 0.08 #2ca02c 30
12 Landsat 4-5 Landsat 4--5 tm TM 3 3 0.63 0.63 0.06 0.06 #d62728 30
13 Landsat 4-5 Landsat 4--5 tm TM 4 4 0.76 0.76 0.14 0.14 #e377c2 30
14 Landsat 4-5 Landsat 4--5 tm TM 5 5 1.55 1.55 0.2 0.2 #ff7f0e 30
15 Landsat 4-5 Landsat 4--5 tm TM 7 6 2.08 10.4 0.27 2.1 #7f7f7f #8c564b 30 120
16 Landsat 7 Landsat 4--5 etm TM 1 7 0.45 2.08 0.07 0.27 #1f77b4 #7f7f7f 30
17 Landsat 7 Landsat 6 etm ETM 2 1 0.52 0.45 0.08 0.07 #2ca02c #1f77b4 30
18 Landsat 7 Landsat 6 etm ETM 3 2 0.63 0.52 0.06 0.08 #d62728 #2ca02c 30
19 Landsat 7 Landsat 6 etm ETM 4 3 0.77 0.63 0.13 0.06 #e377c2 #d62728 30
20 Landsat 7 Landsat 6 etm ETM 5 4 1.55 0.76 0.2 0.14 #ff7f0e #e377c2 30
21 Landsat 7 Landsat 6 etm ETM 7 5 2.09 1.55 0.26 0.2 #7f7f7f #ff7f0e 30
22 Landsat 7 Landsat 6 etm ETM 8 6 0.52 10.4 0.38 2.1 #bcbd22 #8c564b 15 120
23 Landsat 8-9 Landsat 6 oli ETM 1 7 0.43 2.08 0.02 0.27 #17becf #7f7f7f 30
24 Landsat 8-9 Landsat 6 oli ETM 2 8 0.45 0.52 0.06 0.38 #1f77b4 #bcbd22 30 15
25 Landsat 8-9 Landsat 7 oli ETM+ 3 1 0.53 0.45 0.06 0.07 #2ca02c #1f77b4 30
26 Landsat 8-9 Landsat 7 oli ETM+ 4 2 0.64 0.52 0.03 0.08 #d62728 #2ca02c 30
27 Landsat 8-9 Landsat 7 oli ETM+ 5 3 0.85 0.63 0.03 0.06 #e377c2 #d62728 30
28 Landsat 8-9 Landsat 7 oli ETM+ 6 4 1.57 0.77 0.08 0.13 #ff7f0e #e377c2 30
29 Landsat 8-9 Landsat 7 oli ETM+ 7 5 2.11 1.55 0.18 0.2 #7f7f7f #ff7f0e 30
30 Landsat 8-9 Landsat 7 oli ETM+ 9 6 1.36 10.4 0.02 2.1 #9467bd #8c564b 30 60
31 Landsat 8-9 Landsat 7 oli ETM+ 8 7 0.5 2.08 0.18 0.27 #bcbd22 #7f7f7f 15 30
32 Landsat 7 ETM+ 8 0.52 0.38 #bcbd22 15
33 Landsat 8--9 OLI/TIRS 1 0.43 0.02 #17becf 30
34 Landsat 8--9 OLI/TIRS 2 0.45 0.06 #1f77b4 30
35 Landsat 8--9 OLI/TIRS 3 0.53 0.06 #2ca02c 30
36 Landsat 8--9 OLI/TIRS 4 0.64 0.03 #d62728 30
37 Landsat 8--9 OLI/TIRS 5 0.85 0.03 #e377c2 30
38 Landsat 8--9 OLI/TIRS 6 1.57 0.08 #ff7f0e 30
39 Landsat 8--9 OLI/TIRS 7 2.11 0.18 #7f7f7f 30
40 Landsat 8--9 OLI/TIRS 8 0.5 0.18 #bcbd22 15
41 Landsat 8--9 OLI/TIRS 9 1.36 0.02 #9467bd 30
42 Landsat 8--9 OLI/TIRS 10 10.6 0.59 #8c564b 100
43 Landsat 8--9 OLI/TIRS 11 11.5 1.01 #8c564b 100
44
45
46
47
48
49
50

Просмотреть файл

@ -3,67 +3,109 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
"""Plot Landsat band wavelengths and resolutions."""
import argparse
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
# https://www.usgs.gov/faqs/what-are-band-designations-landsat-satellites
df = pd.read_csv("band_data.csv")
# Match NeurIPS template
plt.rcParams.update(
{
"font.family": "Times New Roman",
"font.size": 10,
"axes.labelsize": 10,
"text.usetex": True,
}
)
bar_height = 5
# This dictionary maps a sensor and resolution to a y location on the plot
bar_to_height_map = {
("tm", 120): 50,
("etm", 60): 34,
("tirs", 100): 12,
("mss", 60): 60,
("tm", 30): 44,
("etm", 30): 28,
("etm", 15): 22,
("oli", 30): 6,
("oli", 15): 0,
}
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter, description=__doc__
)
parser.add_argument("skip", nargs="*", help="sensors to skip", metavar="SENSOR")
parser.add_argument(
"--fig-height", default=5, type=float, help="height of figure in inches"
)
parser.add_argument("--bar-start", default=1, type=float, help="height of first bar")
parser.add_argument("--bar-height", default=3, type=float, help="height of each bar")
parser.add_argument(
"--bar-sep", default=3.5, type=float, help="separation between bars"
)
parser.add_argument(
"--bar-jump", default=2.6, type=float, help="additional height for narrow bars"
)
parser.add_argument(
"--sensor-sep", default=2, type=float, help="separation between sensors"
)
args = parser.parse_args()
fig, (ax1, ax2) = plt.subplots(1, 2, gridspec_kw={"width_ratios": [4, 1]})
fig.subplots_adjust(wspace=0.05)
# https://www.usgs.gov/landsat-missions/landsat-satellite-missions
df = pd.read_csv("band_data.csv", skip_blank_lines=True)
df = df.iloc[::-1]
for sensor_name, group1 in df.groupby("Sensor name"):
for resolution, group2 in group1.groupby("Resolution (m)"):
# Plot one row of data
y_position = bar_to_height_map[(sensor_name, resolution)]
fig, ax = plt.subplots(figsize=(5.5, args.fig_height))
ax1, ax2 = fig.subplots(nrows=1, ncols=2, gridspec_kw={"width_ratios": [3, 1]})
sensor_names: list[str] = []
sensor_ylocs: list[float] = []
res_names: list[int] = []
res_ylocs: list[float] = []
bar_min = args.bar_start
# For each satellite/sensor
for (satellite, sensor), group1 in df.groupby(["Satellite", "Sensor"], sort=False):
if sensor in args.skip:
continue
sensor_names.append(f"{satellite}\n({sensor})")
sensor_yloc = 0.0
res_count = 0
# For each resolution
for res, group2 in group1.groupby("Resolution (m)"):
res_names.append(res)
res_ylocs.append(bar_min)
if len(group2) > res_count:
sensor_yloc = bar_min
res_count = len(group2)
# For each band
for i in range(group2.shape[0]):
row = group2.iloc[i]
wavelength_start = row["Wavelength start (μm)"]
wavelength_width = row["Wavelength width"]
wavelength_start = row["Wavelength Start (μm)"]
wavelength_width = row["Wavelength Width"]
color = row["Color"]
band_number = row["Band number"]
band = row["Band"]
# We've split the plot into two parts as the thermal bands are > 10μm
# while the other bands are < 3μm
y = bar_min + args.bar_height / 2
if wavelength_width < 0.05:
y += args.bar_jump
if wavelength_start < 10:
ax1.broken_barh(
[[wavelength_start, wavelength_width]],
[y_position, bar_height],
[bar_min, args.bar_height],
edgecolor="k",
facecolors=color,
linewidth=0.5,
alpha=0.8,
)
if wavelength_width < 0.05:
y = y_position + 6.5
else:
y = y_position + 2.25
ax1.text(
wavelength_start + (wavelength_width / 2),
y,
str(band_number),
band,
horizontalalignment="center",
verticalalignment="center",
verticalalignment="center_baseline",
)
else:
ax2.broken_barh(
[[wavelength_start, wavelength_width]],
[y_position, bar_height],
[bar_min, args.bar_height],
edgecolor="k",
facecolors=color,
linewidth=0.5,
@ -71,37 +113,36 @@ for sensor_name, group1 in df.groupby("Sensor name"):
)
ax2.text(
wavelength_start + (wavelength_width / 2),
y_position + 2.25,
str(band_number),
y,
band,
horizontalalignment="center",
verticalalignment="center",
verticalalignment="center_baseline",
)
bar_min += args.bar_sep
bar_min += args.sensor_sep
sensor_ylocs.append(sensor_yloc)
# Labels
fig.supxlabel("Wavelength (μm)")
ax.set_xlabel(r"Wavelength (\textmu m)")
ax.set_xticks([0.5], labels=[0.5], alpha=0)
ax.set_yticks([0], labels=[0], alpha=0)
ax.spines[["bottom", "left", "top", "right"]].set_visible(False)
ax1.set_ylabel("Satellite (Sensor)")
ax1.set_yticks([62.5, 46.5, 30.5, 8.5])
ax1.set_yticklabels(
[
"Landsat 1–5\n(MSS)",
"Landsat 4–5\n(TM)",
"Landsat 7\n(ETM+)",
"Landsat 8–9\n(OLI+TIRS)",
]
)
ax1.set_ylim(-5, 70)
ax1.spines.right.set_visible(False)
ax1.spines.top.set_visible(False)
ax1.set_yticks(np.array(sensor_ylocs) + args.bar_height / 2)
ax1.set_yticklabels(sensor_names)
ax1.set_ylim(0, max(res_ylocs) + args.bar_height + args.bar_start)
ax1.spines[["left", "top", "right"]].set_visible(False)
ax1.tick_params(axis="both", which="both", left=False)
ax2.set_xlim(10.1, 12.8)
ax2.set_ylim(0, max(res_ylocs) + args.bar_height + args.bar_start)
ax2.yaxis.set_label_position("right")
ax2.yaxis.tick_right()
ax2.set_ylabel("Resolution (m)")
ax2.set_yticks([62.5, 52.5, 46.5, 36.5, 30.5, 24.5, 14.5, 8.5, 2.5])
ax2.set_yticklabels([60, 120, 30, 60, 30, 15, 100, 30, 15])
ax2.set_ylim(-5, 70)
ax2.spines.left.set_visible(False)
ax2.spines.top.set_visible(False)
ax2.set_yticks(np.array(res_ylocs) + args.bar_height / 2)
ax2.set_yticklabels(res_names)
ax2.spines[["left", "top"]].set_visible(False)
# Draw axis break symbol
d = 2
@ -118,4 +159,5 @@ ax1.plot(1, 0, transform=ax1.transAxes, **kwargs)
ax2.plot(0, 0, transform=ax2.transAxes, **kwargs)
plt.tight_layout()
plt.subplots_adjust(wspace=0.1)
plt.show()