ECOv003-L2T-STARS 1.0.1__py3-none-any.whl → 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ECOv003_L2T_STARS/BRDF/BRDF.py +57 -0
- ECOv003_L2T_STARS/BRDF/SZA.py +65 -0
- ECOv003_L2T_STARS/BRDF/__init__.py +1 -0
- ECOv003_L2T_STARS/BRDF/statistical_radiative_transport.txt +90 -0
- ECOv003_L2T_STARS/BRDF/version.txt +1 -0
- ECOv003_L2T_STARS/ECOv003_DL.py +527 -0
- ECOv003_L2T_STARS/ECOv003_DL.xml +47 -0
- ECOv003_L2T_STARS/ECOv003_L2T_STARS.py +162 -0
- ECOv003_L2T_STARS/ECOv003_L2T_STARS.xml +47 -0
- ECOv003_L2T_STARS/L2TSTARSConfig.py +188 -0
- ECOv003_L2T_STARS/L2T_STARS.py +489 -0
- ECOv003_L2T_STARS/LPDAAC/LPDAACDataPool.py +444 -0
- ECOv003_L2T_STARS/LPDAAC/__init__.py +9 -0
- ECOv003_L2T_STARS/LPDAAC/version.txt +1 -0
- ECOv003_L2T_STARS/Manifest.toml +2332 -0
- ECOv003_L2T_STARS/Project.toml +14 -0
- ECOv003_L2T_STARS/VIIRS/VIIRSDataPool.py +294 -0
- ECOv003_L2T_STARS/VIIRS/VIIRSDownloader.py +26 -0
- ECOv003_L2T_STARS/VIIRS/VIIRS_CMR_LOGIN.py +36 -0
- ECOv003_L2T_STARS/VIIRS/VNP09GA.py +1277 -0
- ECOv003_L2T_STARS/VIIRS/VNP43IA4.py +288 -0
- ECOv003_L2T_STARS/VIIRS/VNP43MA3.py +323 -0
- ECOv003_L2T_STARS/VIIRS/__init__.py +9 -0
- ECOv003_L2T_STARS/VIIRS/version.txt +1 -0
- ECOv003_L2T_STARS/VNP43NRT/VNP43NRT.py +863 -0
- ECOv003_L2T_STARS/VNP43NRT/__init__.py +1 -0
- ECOv003_L2T_STARS/VNP43NRT/process_VNP43NRT.jl +169 -0
- ECOv003_L2T_STARS/VNP43NRT/version.txt +1 -0
- ECOv003_L2T_STARS/VNP43NRT_jl/Manifest.toml +995 -0
- ECOv003_L2T_STARS/VNP43NRT_jl/Project.toml +15 -0
- ECOv003_L2T_STARS/VNP43NRT_jl/__init__.py +0 -0
- ECOv003_L2T_STARS/VNP43NRT_jl/instantiate.jl +25 -0
- ECOv003_L2T_STARS/VNP43NRT_jl/instantiate.py +13 -0
- ECOv003_L2T_STARS/VNP43NRT_jl/src/VNP43NRT.jl +411 -0
- ECOv003_L2T_STARS/VNP43NRT_jl/src/__init__.py +0 -0
- ECOv003_L2T_STARS/__init__.py +3 -0
- ECOv003_L2T_STARS/calibrate_fine_to_coarse.py +60 -0
- ECOv003_L2T_STARS/constants.py +38 -0
- ECOv003_L2T_STARS/daterange/__init__.py +1 -0
- ECOv003_L2T_STARS/daterange/daterange.py +35 -0
- ECOv003_L2T_STARS/generate_L2T_STARS_runconfig.py +249 -0
- ECOv003_L2T_STARS/generate_NDVI_coarse_directory.py +21 -0
- ECOv003_L2T_STARS/generate_NDVI_coarse_image.py +30 -0
- ECOv003_L2T_STARS/generate_NDVI_fine_directory.py +14 -0
- ECOv003_L2T_STARS/generate_NDVI_fine_image.py +28 -0
- ECOv003_L2T_STARS/generate_STARS_inputs.py +231 -0
- ECOv003_L2T_STARS/generate_albedo_coarse_directory.py +18 -0
- ECOv003_L2T_STARS/generate_albedo_coarse_image.py +30 -0
- ECOv003_L2T_STARS/generate_albedo_fine_directory.py +17 -0
- ECOv003_L2T_STARS/generate_albedo_fine_image.py +30 -0
- ECOv003_L2T_STARS/generate_filename.py +37 -0
- ECOv003_L2T_STARS/generate_input_staging_directory.py +23 -0
- ECOv003_L2T_STARS/generate_model_state_tile_date_directory.py +28 -0
- ECOv003_L2T_STARS/generate_output_directory.py +28 -0
- ECOv003_L2T_STARS/install_STARS_jl.py +43 -0
- ECOv003_L2T_STARS/instantiate_STARS_jl.py +38 -0
- ECOv003_L2T_STARS/load_prior.py +248 -0
- ECOv003_L2T_STARS/prior.py +56 -0
- ECOv003_L2T_STARS/process_ECOSTRESS_data_fusion_distributed_bias.jl +420 -0
- ECOv003_L2T_STARS/process_STARS_product.py +507 -0
- ECOv003_L2T_STARS/process_julia_data_fusion.py +110 -0
- ECOv003_L2T_STARS/retrieve_STARS_sources.py +101 -0
- ECOv003_L2T_STARS/runconfig.py +70 -0
- ECOv003_L2T_STARS/timer/__init__.py +1 -0
- ECOv003_L2T_STARS/timer/timer.py +77 -0
- ECOv003_L2T_STARS/version.py +8 -0
- ECOv003_L2T_STARS/version.txt +1 -0
- {ECOv003_L2T_STARS-1.0.1.dist-info → ecov003_l2t_stars-1.1.0.dist-info}/METADATA +30 -23
- ecov003_l2t_stars-1.1.0.dist-info/RECORD +73 -0
- {ECOv003_L2T_STARS-1.0.1.dist-info → ecov003_l2t_stars-1.1.0.dist-info}/WHEEL +1 -1
- ecov003_l2t_stars-1.1.0.dist-info/entry_points.txt +3 -0
- ecov003_l2t_stars-1.1.0.dist-info/top_level.txt +1 -0
- ECOv003_L2T_STARS-1.0.1.dist-info/RECORD +0 -5
- ECOv003_L2T_STARS-1.0.1.dist-info/top_level.txt +0 -1
- {ECOv003_L2T_STARS-1.0.1.dist-info → ecov003_l2t_stars-1.1.0.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,863 @@
|
|
1
|
+
import argparse
|
2
|
+
import json
|
3
|
+
import logging
|
4
|
+
import shutil
|
5
|
+
import subprocess
|
6
|
+
import sys
|
7
|
+
from datetime import date, timedelta, datetime
|
8
|
+
from glob import glob
|
9
|
+
from os.path import abspath, expanduser, join, basename, splitext, exists, dirname
|
10
|
+
from typing import Union, List
|
11
|
+
import dateutil
|
12
|
+
import numpy as np
|
13
|
+
from dateutil import parser
|
14
|
+
from matplotlib.colors import Colormap
|
15
|
+
|
16
|
+
import colored_logging as cl
|
17
|
+
|
18
|
+
import rasters
|
19
|
+
from rasters import Raster, RasterGeometry, Point, Polygon
|
20
|
+
from GEOS5FP import GEOS5FP, FailedGEOS5FPDownload
|
21
|
+
from modland import find_modland_tiles, parsehv, generate_modland_grid
|
22
|
+
|
23
|
+
from ..BRDF import bidirectional_reflectance
|
24
|
+
from ..BRDF.SZA import calculate_SZA
|
25
|
+
from ..VIIRS import VIIRSDownloaderAlbedo, VIIRSDownloaderNDVI
|
26
|
+
from ..VIIRS.VNP09GA import VNP09GA, VNP09GAGranule, ALBEDO_COLORMAP, NDVI_COLORMAP, VIIRSUnavailableError
|
27
|
+
from ..daterange import date_range
|
28
|
+
from ..timer import Timer
|
29
|
+
|
30
|
+
DEFAULT_WEIGHTED = True
|
31
|
+
DEFAULT_SCALE = 1.87
|
32
|
+
|
33
|
+
with open(join(abspath(dirname(__file__)), "version.txt")) as f:
|
34
|
+
version = f.read()
|
35
|
+
|
36
|
+
logger = logging.getLogger(__name__)
|
37
|
+
|
38
|
+
def install_VNP43NRT_jl(
|
39
|
+
package_location: str = "https://github.com/STARS-Data-Fusion/VNP43NRT.jl",
|
40
|
+
environment_name: str = "@ECOv002-L2T-STARS"):
|
41
|
+
"""
|
42
|
+
Installs the VNP43NRT.jl package from GitHub into a shared environment.
|
43
|
+
|
44
|
+
Args:
|
45
|
+
github_url: The URL of the GitHub repository containing VNP43NRT.jl.
|
46
|
+
Defaults to "https://github.com/STARS-Data-Fusion/VNP43NRT.jl".
|
47
|
+
environment_name: The name of the shared Julia environment to install the
|
48
|
+
package into. Defaults to "@ECOv002-L2T-STARS".
|
49
|
+
|
50
|
+
Returns:
|
51
|
+
A CompletedProcess object containing information about the execution of the Julia command.
|
52
|
+
"""
|
53
|
+
|
54
|
+
julia_command = [
|
55
|
+
"julia",
|
56
|
+
"-e",
|
57
|
+
f'using Pkg; Pkg.activate("{environment_name}"); Pkg.develop(url="{package_location}")'
|
58
|
+
]
|
59
|
+
|
60
|
+
result = subprocess.run(julia_command, capture_output=True, text=True)
|
61
|
+
|
62
|
+
if result.returncode == 0:
|
63
|
+
print(f"VNP43NRT.jl installed successfully in environment '{environment_name}'!")
|
64
|
+
else:
|
65
|
+
print("Error installing VNP43NRT.jl:")
|
66
|
+
print(result.stderr)
|
67
|
+
|
68
|
+
return result
|
69
|
+
|
70
|
+
def instantiate_VNP43NRT_jl(package_location: str):
|
71
|
+
"""
|
72
|
+
Activates the package_location directory as the active project and instantiates it.
|
73
|
+
|
74
|
+
Args:
|
75
|
+
package_location: The directory of the Julia package to activate and instantiate.
|
76
|
+
|
77
|
+
Returns:
|
78
|
+
A CompletedProcess object containing information about the execution of the Julia command.
|
79
|
+
"""
|
80
|
+
|
81
|
+
julia_command = [
|
82
|
+
"julia",
|
83
|
+
"-e",
|
84
|
+
f'using Pkg; Pkg.activate("{package_location}"); Pkg.instantiate()'
|
85
|
+
]
|
86
|
+
|
87
|
+
result = subprocess.run(julia_command, capture_output=True, text=True)
|
88
|
+
|
89
|
+
if result.returncode == 0:
|
90
|
+
print(f"VNP43NRT.jl instantiated successfully in directory '{package_location}'!")
|
91
|
+
else:
|
92
|
+
print("Error instantiating VNP43NRT.jl:")
|
93
|
+
print(result.stderr)
|
94
|
+
|
95
|
+
return result
|
96
|
+
|
97
|
+
def process_julia_BRDF(
|
98
|
+
band: str,
|
99
|
+
h: int,
|
100
|
+
v: int,
|
101
|
+
tile_width_cells: int,
|
102
|
+
start_date: date,
|
103
|
+
end_date: date,
|
104
|
+
reflectance_directory: str,
|
105
|
+
solar_zenith_directory: str,
|
106
|
+
sensor_zenith_directory: str,
|
107
|
+
relative_azimuth_directory: str,
|
108
|
+
SZA_filename: str,
|
109
|
+
output_directory: str):
|
110
|
+
parent_directory = abspath(join(dirname(__file__), ".."))
|
111
|
+
julia_source_directory = join(parent_directory, "VNP43NRT_jl")
|
112
|
+
julia_script_filename = join(abspath(dirname(__file__)), "process_VNP43NRT.jl")
|
113
|
+
|
114
|
+
instantiate_VNP43NRT_jl(julia_source_directory)
|
115
|
+
|
116
|
+
command = f'julia "{julia_script_filename}" "{band}" "{h}" "{v}" "{tile_width_cells}" "{start_date:%Y-%m-%d}" "{end_date:%Y-%m-%d}" "{reflectance_directory}" "{solar_zenith_directory}" "{sensor_zenith_directory}" "{relative_azimuth_directory}" "{SZA_filename}" "{output_directory}"'
|
117
|
+
logger.info(command)
|
118
|
+
subprocess.run(command, shell=True)
|
119
|
+
|
120
|
+
class BRDFRetrievalFailed(RuntimeError):
|
121
|
+
pass
|
122
|
+
|
123
|
+
class BRDFParameters:
|
124
|
+
def __init__(
|
125
|
+
self,
|
126
|
+
WSA: Raster,
|
127
|
+
BSA: Raster,
|
128
|
+
NBAR: Raster,
|
129
|
+
WSA_SE: Raster,
|
130
|
+
BSA_SE: Raster,
|
131
|
+
NBAR_SE: Raster,
|
132
|
+
BRDF_SE: Raster,
|
133
|
+
BRDF_R2: Raster,
|
134
|
+
count: Raster,
|
135
|
+
filter_invalid=True):
|
136
|
+
|
137
|
+
if filter_invalid:
|
138
|
+
WSA = rasters.where((WSA < 0) | (WSA > 1), np.nan, WSA)
|
139
|
+
BSA = rasters.where((BSA < 0) | (BSA > 1), np.nan, BSA)
|
140
|
+
NBAR = rasters.where((NBAR < 0) | (NBAR > 1), np.nan, NBAR)
|
141
|
+
|
142
|
+
self.WSA = WSA
|
143
|
+
self.BSA = BSA
|
144
|
+
self.NBAR = NBAR
|
145
|
+
self.WSA_SE = WSA_SE
|
146
|
+
self.BSA_SE = BSA_SE
|
147
|
+
self.NBAR_SE = NBAR_SE
|
148
|
+
self.BRDF_SE = BRDF_SE
|
149
|
+
self.BRDF_R2 = BRDF_R2
|
150
|
+
self.count = count
|
151
|
+
|
152
|
+
|
153
|
+
class VNP43NRTGranule:
|
154
|
+
def __init__(self, directory: str):
|
155
|
+
self._directory = abspath(directory)
|
156
|
+
|
157
|
+
def __repr__(self):
|
158
|
+
return f"VNP43NRTGranule({self.directory})"
|
159
|
+
|
160
|
+
@property
|
161
|
+
def directory(self):
|
162
|
+
return self._directory
|
163
|
+
|
164
|
+
@property
|
165
|
+
def granule_ID(self) -> str:
|
166
|
+
return splitext(basename(self.directory))[0]
|
167
|
+
|
168
|
+
@property
|
169
|
+
def tile(self):
|
170
|
+
return self.granule_ID.split("_")[2]
|
171
|
+
|
172
|
+
@property
|
173
|
+
def hv(self):
|
174
|
+
return parsehv(self.tile)
|
175
|
+
|
176
|
+
@property
|
177
|
+
def h(self):
|
178
|
+
return self.hv[0]
|
179
|
+
|
180
|
+
@property
|
181
|
+
def v(self):
|
182
|
+
return self.hv[1]
|
183
|
+
|
184
|
+
@property
|
185
|
+
def date_UTC(self):
|
186
|
+
return datetime.strptime(self.granule_ID.split("_")[1][1:], "%Y%j")
|
187
|
+
|
188
|
+
@property
|
189
|
+
def variables(self) -> List[str]:
|
190
|
+
return [
|
191
|
+
splitext(basename(filename))[0].split("_")[-1]
|
192
|
+
for filename
|
193
|
+
in glob(join(self.directory, "*.tif"))
|
194
|
+
]
|
195
|
+
|
196
|
+
def variable_filename(self, variable_name: str) -> str:
|
197
|
+
return join(self.directory, f"{self.granule_ID}_{variable_name}.tif")
|
198
|
+
|
199
|
+
def variable(self, variable_name: str, geometry: RasterGeometry = None, cmap: Colormap = None) -> Raster:
|
200
|
+
return Raster.open(self.variable_filename(variable_name), geometry=geometry, cmap=cmap)
|
201
|
+
|
202
|
+
def get_NDVI(self, geometry: RasterGeometry = None, cmap: Colormap = NDVI_COLORMAP) -> Raster:
|
203
|
+
return self.variable("NDVI", geometry=geometry, cmap=cmap)
|
204
|
+
|
205
|
+
NDVI = property(get_NDVI)
|
206
|
+
|
207
|
+
def get_albedo(self, geometry: RasterGeometry = None, cmap: Colormap = ALBEDO_COLORMAP) -> Raster:
|
208
|
+
return self.variable("albedo", geometry=geometry, cmap=cmap)
|
209
|
+
|
210
|
+
albedo = property(get_albedo)
|
211
|
+
|
212
|
+
def BSA(self, band: int, geometry: RasterGeometry = None) -> Raster:
|
213
|
+
return self.variable(f"BSA_M{band}", geometry=geometry)
|
214
|
+
|
215
|
+
def WSA(self, band: int, geometry: RasterGeometry = None) -> Raster:
|
216
|
+
return self.variable(f"WSA_M{band}", geometry=geometry)
|
217
|
+
|
218
|
+
def NBAR(self, band: int, geometry: RasterGeometry = None) -> Raster:
|
219
|
+
return self.variable(f"NBAR_I{band}", geometry=geometry)
|
220
|
+
|
221
|
+
def add_layer(self, variable_name: str, image: Raster) -> str:
|
222
|
+
filename = self.variable_filename(variable_name)
|
223
|
+
logger.info(f"adding VNP43NRT layer at {cl.place(self.tile)} on {cl.time(self.date_UTC)}: {cl.file(filename)}")
|
224
|
+
image.to_COG(filename)
|
225
|
+
|
226
|
+
return filename
|
227
|
+
|
228
|
+
@property
|
229
|
+
def complete(self) -> bool:
|
230
|
+
required_variables = [
|
231
|
+
"NDVI",
|
232
|
+
"NBAR_I1",
|
233
|
+
"NBAR_I2",
|
234
|
+
"albedo"
|
235
|
+
]
|
236
|
+
|
237
|
+
for band in [1, 2, 3, 4, 5, 7, 8, 10, 11]:
|
238
|
+
required_variables.append(f"BSA_M{band}")
|
239
|
+
required_variables.append(f"WSA_M{band}")
|
240
|
+
|
241
|
+
for variable in required_variables:
|
242
|
+
filename = self.variable_filename(variable)
|
243
|
+
|
244
|
+
if not exists(filename):
|
245
|
+
return False
|
246
|
+
|
247
|
+
return True
|
248
|
+
|
249
|
+
|
250
|
+
class AuxiliaryDownloadFailed(ConnectionError):
|
251
|
+
pass
|
252
|
+
|
253
|
+
|
254
|
+
class VNP43NRT(VIIRSDownloaderAlbedo, VIIRSDownloaderNDVI):
|
255
|
+
DEFAULT_VNP09GA_DIRECTORY = "VNP09GA_products"
|
256
|
+
DEFAULT_VNP43NRT_DIRECTORY = "VNP43NRT_products"
|
257
|
+
|
258
|
+
def __init__(
|
259
|
+
self,
|
260
|
+
working_directory: str = None,
|
261
|
+
download_directory: str = None,
|
262
|
+
VNP09GA_directory: str = None,
|
263
|
+
VNP43NRT_directory: str = None,
|
264
|
+
mosaic_directory: str = None,
|
265
|
+
VNP43NRT_staging_directory: str = None,
|
266
|
+
GEOS5FP_connection: GEOS5FP = None,
|
267
|
+
GEOS5FP_download: str = None,
|
268
|
+
GEOS5FP_products: str = None):
|
269
|
+
if working_directory is None:
|
270
|
+
working_directory = VNP09GA.DEFAULT_WORKING_DIRECTORY
|
271
|
+
|
272
|
+
working_directory = abspath(expanduser(working_directory))
|
273
|
+
|
274
|
+
if VNP43NRT_staging_directory is None:
|
275
|
+
VNP43NRT_staging_directory = join(working_directory, "VNP43NRT_staging")
|
276
|
+
|
277
|
+
if VNP09GA_directory is None:
|
278
|
+
VNP09GA_directory = join(working_directory, self.DEFAULT_VNP09GA_DIRECTORY)
|
279
|
+
|
280
|
+
VNP09GA_directory = abspath(expanduser(VNP09GA_directory))
|
281
|
+
|
282
|
+
if VNP43NRT_directory is None:
|
283
|
+
VNP43NRT_directory = join(working_directory, self.DEFAULT_VNP43NRT_DIRECTORY)
|
284
|
+
|
285
|
+
VNP43NRT_directory = abspath(expanduser(VNP43NRT_directory))
|
286
|
+
|
287
|
+
self.vnp09ga = VNP09GA(
|
288
|
+
working_directory=working_directory,
|
289
|
+
download_directory=download_directory,
|
290
|
+
products_directory=VNP09GA_directory,
|
291
|
+
mosaic_directory=mosaic_directory
|
292
|
+
)
|
293
|
+
|
294
|
+
if GEOS5FP_connection is None:
|
295
|
+
GEOS5FP_connection = GEOS5FP(
|
296
|
+
working_directory=working_directory,
|
297
|
+
download_directory=GEOS5FP_download,
|
298
|
+
# products_directory=GEOS5FP_products
|
299
|
+
)
|
300
|
+
|
301
|
+
self.VNP09GA_directory = VNP09GA_directory
|
302
|
+
self.VNP43NRT_directory = VNP43NRT_directory
|
303
|
+
self.GEOS5FP = GEOS5FP_connection
|
304
|
+
self.VNP43NRT_staging_directory = VNP43NRT_staging_directory
|
305
|
+
|
306
|
+
def __repr__(self):
|
307
|
+
display_dict = {
|
308
|
+
"download_directory": self.vnp09ga.download_directory,
|
309
|
+
"VNP09GA_directory": self.VNP09GA_directory,
|
310
|
+
"VNP43NRT_directory": self.VNP43NRT_directory,
|
311
|
+
"mosaic_directory": self.vnp09ga.mosaic_directory
|
312
|
+
}
|
313
|
+
|
314
|
+
display_string = json.dumps(display_dict, indent=2)
|
315
|
+
|
316
|
+
return display_string
|
317
|
+
|
318
|
+
def VNP09GA(
|
319
|
+
self,
|
320
|
+
date_UTC: Union[date, str],
|
321
|
+
tile: str) -> VNP09GAGranule:
|
322
|
+
return self.vnp09ga.granule(
|
323
|
+
date_UTC=date_UTC,
|
324
|
+
tile=tile,
|
325
|
+
)
|
326
|
+
|
327
|
+
def prefetch_VNP09GA(
|
328
|
+
self,
|
329
|
+
start_date: Union[date, str],
|
330
|
+
end_date: Union[date, str],
|
331
|
+
geometry: Point or Polygon or RasterGeometry = None):
|
332
|
+
self.vnp09ga.prefetch_VNP09GA(
|
333
|
+
start_date,
|
334
|
+
end_date,
|
335
|
+
geometry,
|
336
|
+
)
|
337
|
+
|
338
|
+
def generate_staging_directory(self, tile: str, variable: str) -> str:
|
339
|
+
return join(self.VNP43NRT_staging_directory, tile, variable)
|
340
|
+
|
341
|
+
def generate_staging_filename(self, tile: str, processing_date, variable: str) -> str:
|
342
|
+
return join(self.generate_staging_directory(tile, variable), f"{processing_date:%Y-%m-%d}_{variable}.tif")
|
343
|
+
|
344
|
+
def BRDF_parameters(
|
345
|
+
self,
|
346
|
+
date_UTC: Union[date, str],
|
347
|
+
tile: str,
|
348
|
+
band: str):
|
349
|
+
DTYPE = np.float64
|
350
|
+
|
351
|
+
if isinstance(date_UTC, str):
|
352
|
+
date_UTC = parser.parse(date_UTC).date()
|
353
|
+
|
354
|
+
logger.info(f"processing BRDF for band {band} at tile {tile} on date {cl.time(date_UTC)}")
|
355
|
+
|
356
|
+
end_date = date_UTC
|
357
|
+
start_date = date_UTC - timedelta(days=16)
|
358
|
+
|
359
|
+
band_type = band[0]
|
360
|
+
|
361
|
+
h = int(tile[1:3])
|
362
|
+
v = int(tile[4:6])
|
363
|
+
|
364
|
+
if band_type == "I":
|
365
|
+
tile_width_cells = 2400
|
366
|
+
elif band_type == "M":
|
367
|
+
tile_width_cells = 1200
|
368
|
+
else:
|
369
|
+
raise ValueError(f"invalid band: {band}")
|
370
|
+
|
371
|
+
grid = generate_modland_grid(h, v, tile_width_cells)
|
372
|
+
|
373
|
+
# reflectance_list = []
|
374
|
+
# solar_zenith_list = []
|
375
|
+
# sensor_zenith_list = []
|
376
|
+
# relative_azimuth_list = []
|
377
|
+
|
378
|
+
granule = self.VNP09GA(date_UTC, tile)
|
379
|
+
geometry = granule.geometry(band)
|
380
|
+
|
381
|
+
for processing_date in date_range(start_date, end_date):
|
382
|
+
logger.info(f"retrieving VNP09GA for VNP43NRT at {cl.place(tile)} on {cl.time(date_UTC)}")
|
383
|
+
|
384
|
+
# TODO replace the lists with directories of GeoTIFFs staged for VNP43NRT_jl
|
385
|
+
try:
|
386
|
+
granule = self.VNP09GA(processing_date, tile)
|
387
|
+
|
388
|
+
reflectance_filename = self.generate_staging_filename(tile, processing_date, band)
|
389
|
+
|
390
|
+
if exists(reflectance_filename):
|
391
|
+
logger.info(f"previously generated {band} reflectance on {processing_date}: {reflectance_filename}")
|
392
|
+
# reflectance_raster = Raster.open(reflectance_filename)
|
393
|
+
else:
|
394
|
+
logger.info(f"generating {band} reflectance on {processing_date}")
|
395
|
+
reflectance_raster = granule.band(band)
|
396
|
+
logger.info(f"writing {band} reflectance on {processing_date}: {reflectance_filename}")
|
397
|
+
reflectance_raster.to_geotiff(reflectance_filename)
|
398
|
+
|
399
|
+
# reflectance_list.append(np.array(reflectance_raster).flatten())
|
400
|
+
|
401
|
+
solar_zenith_filename = self.generate_staging_filename(tile, processing_date, f"{band_type}_solar_zenith")
|
402
|
+
|
403
|
+
if exists(solar_zenith_filename):
|
404
|
+
logger.info(f"previously generated solar zenith on {processing_date}: {solar_zenith_filename}")
|
405
|
+
# solar_zenith_raster = Raster.open(solar_zenith_filename)
|
406
|
+
else:
|
407
|
+
logger.info(f"generating solar zenith on {processing_date}")
|
408
|
+
solar_zenith_raster = granule.solar_zenith(band)
|
409
|
+
logger.info(f"writing solar zenith on {processing_date}: {solar_zenith_filename}")
|
410
|
+
solar_zenith_raster.to_geotiff(solar_zenith_filename)
|
411
|
+
|
412
|
+
# solar_zenith_list.append(np.array(solar_zenith_raster).flatten())
|
413
|
+
|
414
|
+
sensor_zenith_filename = self.generate_staging_filename(tile, processing_date, f"{band_type}_sensor_zenith")
|
415
|
+
|
416
|
+
if exists(sensor_zenith_filename):
|
417
|
+
logger.info(f"previously generated sensor zenith on {processing_date}: {sensor_zenith_filename}")
|
418
|
+
# sensor_zenith_raster = Raster.open(sensor_zenith_filename)
|
419
|
+
else:
|
420
|
+
logger.info(f"generating sensor zenith on {processing_date}")
|
421
|
+
sensor_zenith_raster = granule.sensor_zenith(band)
|
422
|
+
logger.info(f"writing sensor zenith on {processing_date}: {sensor_zenith_filename}")
|
423
|
+
sensor_zenith_raster.to_geotiff(sensor_zenith_filename)
|
424
|
+
|
425
|
+
# sensor_zenith_list.append(np.array(sensor_zenith_raster).flatten())
|
426
|
+
|
427
|
+
relative_azimuth_filename = self.generate_staging_filename(tile, processing_date, f"{band_type}_relative_azimuth")
|
428
|
+
|
429
|
+
if exists(relative_azimuth_filename):
|
430
|
+
logger.info(f"previously generated sensor zenith on {processing_date}: {relative_azimuth_filename}")
|
431
|
+
# relative_azimuth_raster = Raster.open(relative_azimuth_filename)
|
432
|
+
else:
|
433
|
+
logger.info(f"generating sensor zenith on {processing_date}")
|
434
|
+
solar_azimuth = granule.solar_azimuth(band)
|
435
|
+
sensor_azimuth = granule.sensor_azimuth(band)
|
436
|
+
relative_azimuth_raster = Raster(np.abs(solar_azimuth - sensor_azimuth), geometry=sensor_azimuth.geometry)
|
437
|
+
logger.info(f"writing sensor zenith on {processing_date}: {relative_azimuth_filename}")
|
438
|
+
relative_azimuth_raster.to_geotiff(relative_azimuth_filename)
|
439
|
+
|
440
|
+
# relative_azimuth_list.append(np.array(relative_azimuth_raster).flatten())
|
441
|
+
except VIIRSUnavailableError as e:
|
442
|
+
if (datetime.utcnow().date() - processing_date).days > 4:
|
443
|
+
logger.warning(e)
|
444
|
+
continue
|
445
|
+
else:
|
446
|
+
raise e
|
447
|
+
|
448
|
+
# Y = np.stack(reflectance_list).T.astype(DTYPE)
|
449
|
+
# sz = np.stack(solar_zenith_list).T.astype(DTYPE)
|
450
|
+
# vz = np.stack(sensor_zenith_list).T.astype(DTYPE)
|
451
|
+
# rz = np.stack(relative_azimuth_list).T.astype(DTYPE)
|
452
|
+
|
453
|
+
# SZA_filename = self.generate_SZA_filename(date_UTC, band_type)
|
454
|
+
SZA_filename = self.generate_staging_filename(tile, date_UTC, f"{band_type}_solar_zenith_noon")
|
455
|
+
|
456
|
+
if exists(SZA_filename):
|
457
|
+
logger.info(f"solar zenith noon file already exists: {SZA_filename}")
|
458
|
+
else:
|
459
|
+
doy = date_UTC.timetuple().tm_yday
|
460
|
+
SZA = calculate_SZA(doy, 12, grid)
|
461
|
+
logger.info(f"writing solar zenith noon: {SZA_filename}")
|
462
|
+
SZA.to_geotiff(SZA_filename)
|
463
|
+
|
464
|
+
# soz_noon = np.array(SZA).flatten()
|
465
|
+
|
466
|
+
logger.info(f"started processing VNP43NRT BRDF parameters at {cl.place(tile)} on {cl.time(date_UTC)}")
|
467
|
+
timer = Timer()
|
468
|
+
|
469
|
+
# TODO replace this with call to VNP43NRT_jl
|
470
|
+
try:
|
471
|
+
# cpp_results = NRT_BRDF_all(
|
472
|
+
# Y=Y,
|
473
|
+
# sz=sz,
|
474
|
+
# vz=vz,
|
475
|
+
# rz=rz,
|
476
|
+
# soz_noon=soz_noon
|
477
|
+
# )
|
478
|
+
reflectance_directory = self.generate_staging_directory(tile, band)
|
479
|
+
solar_zenith_directory = self.generate_staging_directory(tile, f"{band_type}_solar_zenith")
|
480
|
+
sensor_zenith_directory = self.generate_staging_directory(tile, f"{band_type}_sensor_zenith")
|
481
|
+
relative_azimuth_directory = self.generate_staging_directory(tile, f"{band_type}_relative_azimuth")
|
482
|
+
output_directory = self.generate_staging_directory(tile, "output")
|
483
|
+
|
484
|
+
process_julia_BRDF(
|
485
|
+
band=band,
|
486
|
+
h=h,
|
487
|
+
v=v,
|
488
|
+
tile_width_cells=tile_width_cells,
|
489
|
+
start_date=start_date,
|
490
|
+
end_date=end_date,
|
491
|
+
reflectance_directory=reflectance_directory,
|
492
|
+
solar_zenith_directory=solar_zenith_directory,
|
493
|
+
sensor_zenith_directory=sensor_zenith_directory,
|
494
|
+
relative_azimuth_directory=relative_azimuth_directory,
|
495
|
+
SZA_filename=SZA_filename,
|
496
|
+
output_directory=output_directory
|
497
|
+
)
|
498
|
+
|
499
|
+
WSA = Raster.open(join(output_directory, f"{date_UTC:%Y-%m-%d}_WSA.tif"))
|
500
|
+
BSA = Raster.open(join(output_directory, f"{date_UTC:%Y-%m-%d}_BSA.tif"))
|
501
|
+
NBAR = Raster.open(join(output_directory, f"{date_UTC:%Y-%m-%d}_NBAR.tif"))
|
502
|
+
WSA_SE = Raster.open(join(output_directory, f"{date_UTC:%Y-%m-%d}_WSA_SE.tif"))
|
503
|
+
BSA_SE = Raster.open(join(output_directory, f"{date_UTC:%Y-%m-%d}_BSA_SE.tif"))
|
504
|
+
NBAR_SE = Raster.open(join(output_directory, f"{date_UTC:%Y-%m-%d}_NBAR_SE.tif"))
|
505
|
+
BRDF_SE = Raster.open(join(output_directory, f"{date_UTC:%Y-%m-%d}_BRDF_SE.tif"))
|
506
|
+
BRDF_R2 = Raster.open(join(output_directory, f"{date_UTC:%Y-%m-%d}_BRDF_R2.tif"))
|
507
|
+
count = Raster.open(join(output_directory, f"{date_UTC:%Y-%m-%d}_count.tif"))
|
508
|
+
|
509
|
+
logger.info(f"removing output directory: {output_directory}")
|
510
|
+
shutil.rmtree(output_directory)
|
511
|
+
except RuntimeError as e:
|
512
|
+
logger.exception(e)
|
513
|
+
# raise BRDFRetrievalFailed(f"BRDF retrival failed for {cl.place(tile)} on {cl.time(date_UTC)} ({cl.time(timer)})")
|
514
|
+
WSA = Raster(np.full(geometry.shape, np.nan, np.float32), geometry=geometry)
|
515
|
+
BSA = Raster(np.full(geometry.shape, np.nan, np.float32), geometry=geometry)
|
516
|
+
NBAR = Raster(np.full(geometry.shape, np.nan, np.float32), geometry=geometry)
|
517
|
+
WSA_SE = Raster(np.full(geometry.shape, np.nan, np.float32), geometry=geometry)
|
518
|
+
BSA_SE = Raster(np.full(geometry.shape, np.nan, np.float32), geometry=geometry)
|
519
|
+
NBAR_SE = Raster(np.full(geometry.shape, np.nan, np.float32), geometry=geometry)
|
520
|
+
BRDF_SE = Raster(np.full(geometry.shape, np.nan, np.float32), geometry=geometry)
|
521
|
+
BRDF_R2 = Raster(np.full(geometry.shape, np.nan, np.float32), geometry=geometry)
|
522
|
+
count = Raster(np.full(geometry.shape, np.nan, np.float32), geometry=geometry)
|
523
|
+
|
524
|
+
BRDF_parameters = BRDFParameters(
|
525
|
+
WSA=WSA,
|
526
|
+
BSA=BSA,
|
527
|
+
NBAR=NBAR,
|
528
|
+
NBAR_SE=NBAR_SE,
|
529
|
+
WSA_SE=WSA_SE,
|
530
|
+
BSA_SE=BSA_SE,
|
531
|
+
BRDF_SE=BRDF_SE,
|
532
|
+
BRDF_R2=BRDF_R2,
|
533
|
+
count=count
|
534
|
+
)
|
535
|
+
|
536
|
+
return BRDF_parameters
|
537
|
+
|
538
|
+
logger.info(
|
539
|
+
f"finished processing VNP43NRT BRDF parameters at {cl.place(tile)} on {cl.time(date_UTC)} ({cl.time(timer)})")
|
540
|
+
|
541
|
+
# TODO replace this with loading the GeoTIFFs written by VNP43NRT_jl
|
542
|
+
# WSA = Raster(cpp_results[:, 0].reshape(geometry.shape), geometry=geometry)
|
543
|
+
# BSA = Raster(cpp_results[:, 1].reshape(geometry.shape), geometry=geometry)
|
544
|
+
# NBAR = Raster(cpp_results[:, 2].reshape(geometry.shape), geometry=geometry)
|
545
|
+
# WSA_SE = Raster(cpp_results[:, 3].reshape(geometry.shape), geometry=geometry)
|
546
|
+
# BSA_SE = Raster(cpp_results[:, 4].reshape(geometry.shape), geometry=geometry)
|
547
|
+
# NBAR_SE = Raster(cpp_results[:, 5].reshape(geometry.shape), geometry=geometry)
|
548
|
+
# BRDF_SE = Raster(cpp_results[:, 6].reshape(geometry.shape), geometry=geometry)
|
549
|
+
# BRDF_R2 = Raster(cpp_results[:, 7].reshape(geometry.shape), geometry=geometry)
|
550
|
+
# count = Raster(cpp_results[:, 8].reshape(geometry.shape), geometry=geometry)
|
551
|
+
|
552
|
+
BRDF_parameters = BRDFParameters(
|
553
|
+
WSA=WSA,
|
554
|
+
BSA=BSA,
|
555
|
+
NBAR=NBAR,
|
556
|
+
NBAR_SE=NBAR_SE,
|
557
|
+
WSA_SE=WSA_SE,
|
558
|
+
BSA_SE=BSA_SE,
|
559
|
+
BRDF_SE=BRDF_SE,
|
560
|
+
BRDF_R2=BRDF_R2,
|
561
|
+
count=count
|
562
|
+
)
|
563
|
+
|
564
|
+
# logger.info(f"removing staging_directory: {self.VNP43NRT_staging_directory}")
|
565
|
+
# shutil.rmtree(self.VNP43NRT_staging_directory, ignore_errors=True)
|
566
|
+
|
567
|
+
return BRDF_parameters
|
568
|
+
|
569
|
+
def granule_ID(
|
570
|
+
self,
|
571
|
+
date_UTC: Union[date, str],
|
572
|
+
tile: str) -> str:
|
573
|
+
if isinstance(date_UTC, str):
|
574
|
+
date_UTC = parser.parse(date_UTC).date()
|
575
|
+
|
576
|
+
granule_ID = f"VNP43NRT_A{date_UTC:%Y%j}_{tile}"
|
577
|
+
|
578
|
+
return granule_ID
|
579
|
+
|
580
|
+
def granule_directory(
|
581
|
+
self,
|
582
|
+
date_UTC: Union[date, str],
|
583
|
+
tile: str) -> str:
|
584
|
+
return join(self.VNP43NRT_directory, self.granule_ID(date_UTC, tile))
|
585
|
+
|
586
|
+
def AOT(self, time_UTC: datetime, geometry: RasterGeometry = None, resampling: str = None) -> Raster:
|
587
|
+
try:
|
588
|
+
return self.GEOS5FP.AOT(time_UTC=time_UTC, geometry=geometry, resampling=resampling)
|
589
|
+
except FailedGEOS5FPDownload as e:
|
590
|
+
raise AuxiliaryDownloadFailed("unable to retrieve AOT from GEOS5-FP for VNP43NRT")
|
591
|
+
|
592
|
+
def VNP43NRT(
|
593
|
+
self,
|
594
|
+
date_UTC: Union[date, str],
|
595
|
+
tile: str,
|
596
|
+
diagnostics: bool = False) -> VNP43NRTGranule:
|
597
|
+
if isinstance(date_UTC, str):
|
598
|
+
date_UTC = parser.parse(date_UTC).date()
|
599
|
+
|
600
|
+
logger.info(f"started processing VNP43NRT at {cl.place(tile)} on {cl.time(date_UTC)}")
|
601
|
+
timer = Timer()
|
602
|
+
|
603
|
+
directory = self.granule_directory(
|
604
|
+
date_UTC=date_UTC,
|
605
|
+
tile=tile
|
606
|
+
)
|
607
|
+
|
608
|
+
granule = VNP43NRTGranule(directory)
|
609
|
+
|
610
|
+
if granule.complete:
|
611
|
+
return granule
|
612
|
+
|
613
|
+
for i in (1, 2):
|
614
|
+
BRDF_parameters = self.BRDF_parameters(
|
615
|
+
date_UTC=date_UTC,
|
616
|
+
tile=tile,
|
617
|
+
band=f"I{i}"
|
618
|
+
)
|
619
|
+
|
620
|
+
granule.add_layer(f"NBAR_I{i}", BRDF_parameters.NBAR)
|
621
|
+
|
622
|
+
if diagnostics:
|
623
|
+
granule.add_layer(f"NBARSE_I{i}", BRDF_parameters.NBAR_SE)
|
624
|
+
granule.add_layer(f"WSA_I{i}", BRDF_parameters.WSA)
|
625
|
+
granule.add_layer(f"WSASE_I{i}", BRDF_parameters.WSA_SE)
|
626
|
+
granule.add_layer(f"BSA_I{i}", BRDF_parameters.BSA)
|
627
|
+
granule.add_layer(f"BSASE_I{i}", BRDF_parameters.BSA_SE)
|
628
|
+
granule.add_layer(f"BRDFSE_I{i}", BRDF_parameters.BRDF_SE)
|
629
|
+
granule.add_layer(f"count_I{i}", BRDF_parameters.count)
|
630
|
+
|
631
|
+
NIR = granule.variable("NBAR_I2")
|
632
|
+
red = granule.variable("NBAR_I1")
|
633
|
+
NDVI = rasters.clip((NIR - red) / (NIR + red), -1, 1)
|
634
|
+
granule.add_layer("NDVI", NDVI)
|
635
|
+
|
636
|
+
time_UTC = datetime(date_UTC.year, date_UTC.month, date_UTC.day, 10, 30)
|
637
|
+
geometry = generate_modland_grid(*parsehv(tile), 1200)
|
638
|
+
AOT = self.AOT(time_UTC=time_UTC, geometry=geometry, resampling="cubic")
|
639
|
+
|
640
|
+
if diagnostics:
|
641
|
+
granule.add_layer("AOT", AOT)
|
642
|
+
|
643
|
+
doy = date_UTC.timetuple().tm_yday
|
644
|
+
SZA = calculate_SZA(doy, 10.5, geometry)
|
645
|
+
|
646
|
+
if diagnostics:
|
647
|
+
granule.add_layer("SZA", SZA)
|
648
|
+
|
649
|
+
b = {}
|
650
|
+
|
651
|
+
for m in (1, 2, 3, 4, 5, 7, 8, 10, 11):
|
652
|
+
BRDF_parameters = self.BRDF_parameters(
|
653
|
+
date_UTC=date_UTC,
|
654
|
+
tile=tile,
|
655
|
+
band=f"M{m}"
|
656
|
+
)
|
657
|
+
WSA = BRDF_parameters.WSA
|
658
|
+
granule.add_layer(f"WSA_M{m}", WSA)
|
659
|
+
BSA = BRDF_parameters.BSA
|
660
|
+
granule.add_layer(f"BSA_M{m}", BSA)
|
661
|
+
|
662
|
+
band_albedo = bidirectional_reflectance(
|
663
|
+
white_sky_albedo=WSA,
|
664
|
+
black_sky_albedo=BSA,
|
665
|
+
SZA=SZA,
|
666
|
+
AOT=AOT
|
667
|
+
)
|
668
|
+
|
669
|
+
b[m] = rasters.clip(band_albedo, 0, 1)
|
670
|
+
|
671
|
+
if diagnostics:
|
672
|
+
granule.add_layer(f"WSASE_M{m}", BRDF_parameters.WSA_SE)
|
673
|
+
granule.add_layer(f"BSASE_M{m}", BRDF_parameters.BSA_SE)
|
674
|
+
granule.add_layer(f"BRDFSE_M{m}", BRDF_parameters.BRDF_SE)
|
675
|
+
granule.add_layer(f"NBAR_M{m}", BRDF_parameters.NBAR)
|
676
|
+
granule.add_layer(f"NBARSE_M{m}", BRDF_parameters.NBAR_SE)
|
677
|
+
granule.add_layer(f"count_M{m}", BRDF_parameters.count)
|
678
|
+
|
679
|
+
albedo = 0.2418 * b[1] \
|
680
|
+
- 0.201 * b[2] \
|
681
|
+
+ 0.2093 * b[3] \
|
682
|
+
+ 0.1146 * b[4] \
|
683
|
+
+ 0.1348 * b[5] \
|
684
|
+
+ 0.2251 * b[7] \
|
685
|
+
+ 0.1123 * b[8] \
|
686
|
+
+ 0.0860 * b[10] \
|
687
|
+
+ 0.0803 * b[11] \
|
688
|
+
- 0.0131
|
689
|
+
|
690
|
+
albedo = rasters.clip(albedo, 0, 1)
|
691
|
+
granule.add_layer("albedo", albedo)
|
692
|
+
logger.info(f"finished processing VNP43NRT at {cl.place(tile)} on {cl.time(date_UTC)} ({cl.time(timer)})")
|
693
|
+
|
694
|
+
return granule
|
695
|
+
|
696
|
+
def granule(
|
697
|
+
self,
|
698
|
+
date_UTC: Union[date, str],
|
699
|
+
tile: str) -> VNP43NRTGranule:
|
700
|
+
return self.VNP43NRT(
|
701
|
+
date_UTC=date_UTC,
|
702
|
+
tile=tile
|
703
|
+
)
|
704
|
+
|
705
|
+
def albedo(
|
706
|
+
self,
|
707
|
+
date_UTC: date or str,
|
708
|
+
geometry: RasterGeometry,
|
709
|
+
filename: str = None,
|
710
|
+
save_preview: bool = True) -> Raster:
|
711
|
+
if isinstance(date_UTC, str):
|
712
|
+
date_UTC = parser.parse(date_UTC).date()
|
713
|
+
|
714
|
+
if filename is not None and exists(filename):
|
715
|
+
return Raster.open(filename, cmap=ALBEDO_COLORMAP)
|
716
|
+
|
717
|
+
tiles = sorted(find_modland_tiles(geometry.boundary_latlon.geometry))
|
718
|
+
albedo = None
|
719
|
+
|
720
|
+
for tile in tiles:
|
721
|
+
granule = self.granule(date_UTC=date_UTC, tile=tile)
|
722
|
+
granule_albedo = granule.albedo
|
723
|
+
source_cell_size = granule_albedo.geometry.cell_size
|
724
|
+
dest_cell_size = geometry.cell_size
|
725
|
+
logger.info(f"projecting VIIRS albedo from {cl.val(f'{source_cell_size} m')} to {cl.val(f'{dest_cell_size} m')}")
|
726
|
+
projected_albedo = granule_albedo.to_geometry(geometry)
|
727
|
+
|
728
|
+
if albedo is None:
|
729
|
+
albedo = projected_albedo
|
730
|
+
else:
|
731
|
+
albedo = rasters.where(np.isnan(albedo), projected_albedo, albedo)
|
732
|
+
|
733
|
+
albedo.cmap = ALBEDO_COLORMAP
|
734
|
+
|
735
|
+
if filename is not None:
|
736
|
+
logger.info(f"writing albedo mosaic: {cl.file(filename)}")
|
737
|
+
albedo.to_geotiff(filename)
|
738
|
+
|
739
|
+
if save_preview:
|
740
|
+
albedo.percentilecut.to_geojpeg(filename.replace(".tif", ".jpeg"))
|
741
|
+
|
742
|
+
return albedo
|
743
|
+
|
744
|
+
def NDVI(
|
745
|
+
self,
|
746
|
+
date_UTC: Union[date, str],
|
747
|
+
geometry: RasterGeometry,
|
748
|
+
filename: str = None,
|
749
|
+
resampling: str = None) -> Raster:
|
750
|
+
if isinstance(date_UTC, str):
|
751
|
+
date_UTC = parser.parse(date_UTC).date()
|
752
|
+
|
753
|
+
if filename is not None and exists(filename):
|
754
|
+
return Raster.open(filename, cmap=NDVI_COLORMAP)
|
755
|
+
|
756
|
+
if resampling is None:
|
757
|
+
resampling = self.vnp09ga.resampling
|
758
|
+
|
759
|
+
tiles = sorted(find_modland_tiles(geometry.boundary_latlon.geometry))
|
760
|
+
|
761
|
+
if len(tiles) == 0:
|
762
|
+
raise ValueError("no VIIRS tiles found covering target geometry")
|
763
|
+
|
764
|
+
NDVI = None
|
765
|
+
|
766
|
+
for tile in tiles:
|
767
|
+
granule = self.granule(date_UTC=date_UTC, tile=tile)
|
768
|
+
granule_NDVI = granule.NDVI
|
769
|
+
projected_NDVI = granule_NDVI.to_geometry(geometry, resampling=resampling)
|
770
|
+
|
771
|
+
if NDVI is None:
|
772
|
+
NDVI = projected_NDVI
|
773
|
+
else:
|
774
|
+
NDVI = rasters.where(np.isnan(NDVI), projected_NDVI, NDVI)
|
775
|
+
|
776
|
+
if NDVI is None:
|
777
|
+
raise ValueError("VIIRS NDVI did not generate")
|
778
|
+
|
779
|
+
NDVI.cmap = NDVI_COLORMAP
|
780
|
+
|
781
|
+
if filename is not None:
|
782
|
+
logger.info(f"writing NDVI mosaic: {cl.file(filename)}")
|
783
|
+
NDVI.to_geotiff(filename)
|
784
|
+
|
785
|
+
return NDVI
|
786
|
+
|
787
|
+
|
788
|
+
def main(argv=sys.argv):
|
789
|
+
cl.configure()
|
790
|
+
|
791
|
+
parser = argparse.ArgumentParser(description="run the VNP43NRT BRDF/NDVI/albedo product")
|
792
|
+
|
793
|
+
parser.add_argument(
|
794
|
+
"--date",
|
795
|
+
dest="date"
|
796
|
+
)
|
797
|
+
|
798
|
+
parser.add_argument(
|
799
|
+
"--start",
|
800
|
+
dest="start"
|
801
|
+
)
|
802
|
+
|
803
|
+
parser.add_argument(
|
804
|
+
"--end",
|
805
|
+
dest="end"
|
806
|
+
)
|
807
|
+
|
808
|
+
parser.add_argument(
|
809
|
+
"--tile",
|
810
|
+
dest="tile"
|
811
|
+
)
|
812
|
+
|
813
|
+
parser.add_argument(
|
814
|
+
"--working-directory",
|
815
|
+
dest="working_directory"
|
816
|
+
)
|
817
|
+
|
818
|
+
parser.add_argument(
|
819
|
+
"--diagnostics",
|
820
|
+
action="store_true"
|
821
|
+
)
|
822
|
+
|
823
|
+
args = parser.parse_args(args=argv[1:])
|
824
|
+
|
825
|
+
if args.start is not None:
|
826
|
+
start = args.start
|
827
|
+
elif args.start is None and args.date is not None:
|
828
|
+
start = args.date
|
829
|
+
else:
|
830
|
+
raise ValueError("no dates given")
|
831
|
+
|
832
|
+
end = args.end
|
833
|
+
|
834
|
+
if end is None:
|
835
|
+
end = start
|
836
|
+
|
837
|
+
if args.tile is None:
|
838
|
+
raise ValueError("no tile given")
|
839
|
+
else:
|
840
|
+
tile = args.tile
|
841
|
+
|
842
|
+
working_directory = args.working_directory
|
843
|
+
diagnostics = args.diagnostics
|
844
|
+
|
845
|
+
date_message = f"on {cl.time(start)}" if start == end else f"from {cl.time(start)} to {cl.time(end)}"
|
846
|
+
message = f"running VNP43NRT BRDF/NDVI/albedo at {cl.place(tile)} {date_message}"
|
847
|
+
logger.info(message)
|
848
|
+
|
849
|
+
vnp43nrt = VNP43NRT(working_directory=working_directory)
|
850
|
+
|
851
|
+
start = dateutil.parser.parse(start).date()
|
852
|
+
end = dateutil.parser.parse(end).date()
|
853
|
+
|
854
|
+
for date_UTC in date_range(start, end):
|
855
|
+
granule = vnp43nrt.VNP43NRT(
|
856
|
+
date_UTC=date_UTC,
|
857
|
+
tile=tile,
|
858
|
+
diagnostics=diagnostics
|
859
|
+
)
|
860
|
+
|
861
|
+
|
862
|
+
if __name__ == "__main__":
|
863
|
+
sys.exit(main(argv=sys.argv))
|