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.
Files changed (75) hide show
  1. ECOv003_L2T_STARS/BRDF/BRDF.py +57 -0
  2. ECOv003_L2T_STARS/BRDF/SZA.py +65 -0
  3. ECOv003_L2T_STARS/BRDF/__init__.py +1 -0
  4. ECOv003_L2T_STARS/BRDF/statistical_radiative_transport.txt +90 -0
  5. ECOv003_L2T_STARS/BRDF/version.txt +1 -0
  6. ECOv003_L2T_STARS/ECOv003_DL.py +527 -0
  7. ECOv003_L2T_STARS/ECOv003_DL.xml +47 -0
  8. ECOv003_L2T_STARS/ECOv003_L2T_STARS.py +162 -0
  9. ECOv003_L2T_STARS/ECOv003_L2T_STARS.xml +47 -0
  10. ECOv003_L2T_STARS/L2TSTARSConfig.py +188 -0
  11. ECOv003_L2T_STARS/L2T_STARS.py +489 -0
  12. ECOv003_L2T_STARS/LPDAAC/LPDAACDataPool.py +444 -0
  13. ECOv003_L2T_STARS/LPDAAC/__init__.py +9 -0
  14. ECOv003_L2T_STARS/LPDAAC/version.txt +1 -0
  15. ECOv003_L2T_STARS/Manifest.toml +2332 -0
  16. ECOv003_L2T_STARS/Project.toml +14 -0
  17. ECOv003_L2T_STARS/VIIRS/VIIRSDataPool.py +294 -0
  18. ECOv003_L2T_STARS/VIIRS/VIIRSDownloader.py +26 -0
  19. ECOv003_L2T_STARS/VIIRS/VIIRS_CMR_LOGIN.py +36 -0
  20. ECOv003_L2T_STARS/VIIRS/VNP09GA.py +1277 -0
  21. ECOv003_L2T_STARS/VIIRS/VNP43IA4.py +288 -0
  22. ECOv003_L2T_STARS/VIIRS/VNP43MA3.py +323 -0
  23. ECOv003_L2T_STARS/VIIRS/__init__.py +9 -0
  24. ECOv003_L2T_STARS/VIIRS/version.txt +1 -0
  25. ECOv003_L2T_STARS/VNP43NRT/VNP43NRT.py +863 -0
  26. ECOv003_L2T_STARS/VNP43NRT/__init__.py +1 -0
  27. ECOv003_L2T_STARS/VNP43NRT/process_VNP43NRT.jl +169 -0
  28. ECOv003_L2T_STARS/VNP43NRT/version.txt +1 -0
  29. ECOv003_L2T_STARS/VNP43NRT_jl/Manifest.toml +995 -0
  30. ECOv003_L2T_STARS/VNP43NRT_jl/Project.toml +15 -0
  31. ECOv003_L2T_STARS/VNP43NRT_jl/__init__.py +0 -0
  32. ECOv003_L2T_STARS/VNP43NRT_jl/instantiate.jl +25 -0
  33. ECOv003_L2T_STARS/VNP43NRT_jl/instantiate.py +13 -0
  34. ECOv003_L2T_STARS/VNP43NRT_jl/src/VNP43NRT.jl +411 -0
  35. ECOv003_L2T_STARS/VNP43NRT_jl/src/__init__.py +0 -0
  36. ECOv003_L2T_STARS/__init__.py +3 -0
  37. ECOv003_L2T_STARS/calibrate_fine_to_coarse.py +60 -0
  38. ECOv003_L2T_STARS/constants.py +38 -0
  39. ECOv003_L2T_STARS/daterange/__init__.py +1 -0
  40. ECOv003_L2T_STARS/daterange/daterange.py +35 -0
  41. ECOv003_L2T_STARS/generate_L2T_STARS_runconfig.py +249 -0
  42. ECOv003_L2T_STARS/generate_NDVI_coarse_directory.py +21 -0
  43. ECOv003_L2T_STARS/generate_NDVI_coarse_image.py +30 -0
  44. ECOv003_L2T_STARS/generate_NDVI_fine_directory.py +14 -0
  45. ECOv003_L2T_STARS/generate_NDVI_fine_image.py +28 -0
  46. ECOv003_L2T_STARS/generate_STARS_inputs.py +231 -0
  47. ECOv003_L2T_STARS/generate_albedo_coarse_directory.py +18 -0
  48. ECOv003_L2T_STARS/generate_albedo_coarse_image.py +30 -0
  49. ECOv003_L2T_STARS/generate_albedo_fine_directory.py +17 -0
  50. ECOv003_L2T_STARS/generate_albedo_fine_image.py +30 -0
  51. ECOv003_L2T_STARS/generate_filename.py +37 -0
  52. ECOv003_L2T_STARS/generate_input_staging_directory.py +23 -0
  53. ECOv003_L2T_STARS/generate_model_state_tile_date_directory.py +28 -0
  54. ECOv003_L2T_STARS/generate_output_directory.py +28 -0
  55. ECOv003_L2T_STARS/install_STARS_jl.py +43 -0
  56. ECOv003_L2T_STARS/instantiate_STARS_jl.py +38 -0
  57. ECOv003_L2T_STARS/load_prior.py +248 -0
  58. ECOv003_L2T_STARS/prior.py +56 -0
  59. ECOv003_L2T_STARS/process_ECOSTRESS_data_fusion_distributed_bias.jl +420 -0
  60. ECOv003_L2T_STARS/process_STARS_product.py +507 -0
  61. ECOv003_L2T_STARS/process_julia_data_fusion.py +110 -0
  62. ECOv003_L2T_STARS/retrieve_STARS_sources.py +101 -0
  63. ECOv003_L2T_STARS/runconfig.py +70 -0
  64. ECOv003_L2T_STARS/timer/__init__.py +1 -0
  65. ECOv003_L2T_STARS/timer/timer.py +77 -0
  66. ECOv003_L2T_STARS/version.py +8 -0
  67. ECOv003_L2T_STARS/version.txt +1 -0
  68. {ECOv003_L2T_STARS-1.0.1.dist-info → ecov003_l2t_stars-1.1.0.dist-info}/METADATA +30 -23
  69. ecov003_l2t_stars-1.1.0.dist-info/RECORD +73 -0
  70. {ECOv003_L2T_STARS-1.0.1.dist-info → ecov003_l2t_stars-1.1.0.dist-info}/WHEEL +1 -1
  71. ecov003_l2t_stars-1.1.0.dist-info/entry_points.txt +3 -0
  72. ecov003_l2t_stars-1.1.0.dist-info/top_level.txt +1 -0
  73. ECOv003_L2T_STARS-1.0.1.dist-info/RECORD +0 -5
  74. ECOv003_L2T_STARS-1.0.1.dist-info/top_level.txt +0 -1
  75. {ECOv003_L2T_STARS-1.0.1.dist-info → ecov003_l2t_stars-1.1.0.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,15 @@
1
+ name = "VNP43NRT"
2
+ uuid = "ebab2807-db99-43b5-90ec-49665e98c321"
3
+ authors = ["Gregory Halverson <gregory.h.halverson@jpl.nasa.gov>"]
4
+ version = "0.1.0"
5
+
6
+ [deps]
7
+ ArchGDAL = "c9ce4bd3-c3d5-55b8-8973-c0e20141b8c3"
8
+ Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
9
+ DimensionalData = "0703355e-b756-11e9-17c0-8b28908087d0"
10
+ GDAL = "add2ef01-049f-52c4-9ee2-e494f65e021a"
11
+ Glob = "c27321d9-0574-5035-807b-f59d2c89b15c"
12
+ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
13
+ ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca"
14
+ Rasters = "a3a2b9e3-a471-40c9-b274-f788e487c689"
15
+ Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
File without changes
@@ -0,0 +1,25 @@
1
+ using Pkg
2
+ Pkg.activate(".")
3
+
4
+ if haskey(ENV, "CONDA_PREFIX")
5
+ try
6
+ run(pipeline(`curl "https://raw.githubusercontent.com/JuliaLang/MbedTLS.jl/master/src/cacert.pem"`, stdout="$CONDA_PREFIX/share/julia/cert.pem"))
7
+ catch
8
+ end
9
+ end
10
+
11
+ try
12
+ run(pipeline(`curl "https://raw.githubusercontent.com/JuliaLang/MbedTLS.jl/master/src/cacert.pem"`, stdout="/opt/conda/share/julia/cert.pem"))
13
+ catch
14
+ end
15
+
16
+ Pkg.add("IJulia")
17
+ Pkg.build("IJulia")
18
+ Pkg.add("HDF5")
19
+ Pkg.build("HDF5")
20
+ Pkg.add("CoordinateTransformations")
21
+ Pkg.add(Pkg.PackageSpec(;name="DimensionalData",version="0.24.7"))
22
+ Pkg.add(Pkg.PackageSpec(;name="Rasters",version="0.5.3"))
23
+ Pkg.add(Pkg.PackageSpec(;name="GDAL",version="1.5.1"))
24
+ Pkg.instantiate()
25
+ Pkg.precompile()
@@ -0,0 +1,13 @@
1
+ import subprocess
2
+ from os.path import abspath, dirname
3
+
4
+ directory = abspath(dirname(__file__))
5
+
6
+ def main():
7
+ command = f'cd "{directory}" && julia instantiate.jl'
8
+ print(command)
9
+ # system(command)
10
+ subprocess.run(command, shell=True)
11
+
12
+ if __name__ == "__main":
13
+ main()
@@ -0,0 +1,411 @@
1
+ module VNP43NRT
2
+
3
+ using Dates
4
+ using LinearAlgebra
5
+ using Statistics
6
+ using Rasters
7
+ using DimensionalData.Dimensions.LookupArrays
8
+ using ProgressMeter
9
+
10
+ SINUSOIDAL_CRS = WellKnownText("PROJCS[\"unknown\",GEOGCS[\"unknown\",DATUM[\"unknown\",SPHEROID[\"unknown\",6371007.181,0]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]]],PROJECTION[\"Sinusoidal\"],PARAMETER[\"longitude_of_center\",0],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH]]")
11
+
12
+ function sinusoidal_tile_dims(h::Int, v::Int, tile_width_cells::Int)::Tuple{X,Y}
13
+ # boundaries of sinusodial projection
14
+ GLOBE_UPPER_LEFT_X = -20015109.355798
15
+ GLOBE_UPPER_LEFT_Y = 10007554.677899
16
+ GLOBE_LOWER_RIGHT_X = 20015109.355798
17
+ GLOBE_LOWER_RIGHT_Y = -10007554.677899
18
+
19
+ # size across (width or height) of any equal-area sinusoidal tile
20
+ TILE_SIZE = 1111950.5197665554
21
+
22
+ # rows and columns of sinusoidal tile grid
23
+ TOTAL_ROWS = 18
24
+ TOTAL_COLUMNS = 36
25
+
26
+ cell_size = TILE_SIZE / tile_width_cells
27
+ tile_left_x = GLOBE_UPPER_LEFT_X + h * TILE_SIZE
28
+ tile_right_x = GLOBE_UPPER_LEFT_X + (h + 1) * TILE_SIZE - cell_size
29
+ tile_upper_y = GLOBE_LOWER_RIGHT_Y + (TOTAL_ROWS - v) * TILE_SIZE - cell_size
30
+ tile_lower_y = GLOBE_LOWER_RIGHT_Y + (TOTAL_ROWS - 1 - v) * TILE_SIZE
31
+ sampling = Intervals(Start())
32
+ x_dim = X(Projected(LinRange(tile_left_x, tile_right_x, tile_width_cells), order=ForwardOrdered(), span=Regular(cell_size), sampling=sampling, crs=SINUSOIDAL_CRS))
33
+ y_dim = Y(Projected(LinRange(tile_upper_y, tile_lower_y, tile_width_cells), order=ReverseOrdered(), span=Regular(-cell_size), sampling=sampling, crs=SINUSOIDAL_CRS))
34
+ dims = (x_dim, y_dim)
35
+
36
+ return dims
37
+ end
38
+
39
+ export sinusoidal_tile_dims
40
+
41
+ struct BRDFParameters
42
+ brdf::AbstractMatrix
43
+ se::AbstractVector
44
+ R2::AbstractVector
45
+ uncert::AbstractArray{Float64,3}
46
+ end
47
+
48
+ function zenith_from_solarnoon_time(tm::Float64, lat::Float64, lon::Float64)
49
+ rad = π/180.0
50
+
51
+ Jd = tm/86400.0 + 2440587.5
52
+ Jc = (Jd - 2451545.0)/36525.0
53
+ L0 = mod((280.46646 + Jc * (36000.76983 + 0.0003032 * Jc)),360)
54
+ M = 357.52911 + Jc * (35999.05029 - 0.0001537 * Jc)
55
+ e = 0.016708634 - Jc * (4.2037e-05 + 1.267e-07 * Jc)
56
+ eqctr = sin(rad * M) * (1.914602 - Jc * (0.004817 + 1.4e-05 *
57
+ Jc)) + sin(rad * 2.0 * M) * (0.019993 - 0.000101 * Jc) +
58
+ sin(rad * 3.0 * M) * 0.000289
59
+ lambda0 = L0 + eqctr
60
+ omega = 125.04 - 1934.136 * Jc
61
+ lambda = lambda0 - 0.00569 - 0.00478 * sin(rad * omega)
62
+ seconds = 21.448 - Jc * (46.815 + Jc * (0.00059 - Jc * (0.001813)))
63
+ obliq0 = 23.0 + (26.0 + (seconds/60.0))/60.0
64
+ obliq = obliq0 + 0.00256 * cos(rad * omega)
65
+ y = tan(rad * obliq/2)^2
66
+ eqnTime = 4/rad * (y*sin(rad*2*L0) - 2*e*sin(rad*M) +
67
+ 4*e*y*sin(rad*M)*cos(rad*2*L0) - y^2/2*sin(rad*4*L0) -
68
+ e^2*1.25*sin(rad*2*M))
69
+ solarDec = asin(sin(rad*obliq)*sin(rad*lambda))
70
+ sinSolarDec = sin(solarDec)
71
+ cosSolarDec = cos(solarDec)
72
+ solarTime = (mod(Jd-1/2,1)*1440+eqnTime)/4
73
+ hourAngle = solarTime+lon-180
74
+ cosZenith = sin(rad*lat)*sinSolarDec+cos(rad*lat)*cosSolarDec*cos(rad*hourAngle)
75
+
76
+ if cosZenith < -1
77
+ cosZenith=-1
78
+ end
79
+
80
+ if cosZenith > 1
81
+ cosZenith=1
82
+ end
83
+
84
+ return acos(cosZenith)/rad
85
+ end
86
+
87
+ function zenith_from_solarnoon_vec(tm::Vector{Float64}, lat::Vector{Float64}, lon::Vector{Float64})
88
+ n = length(tm)
89
+ szn = Vector{Float64}(undef, n)
90
+
91
+ for i in 1:n
92
+ szn[i] = zenith_from_solarnoon_time(tm[i], lat[i], lon[i])
93
+ end
94
+
95
+ return szn
96
+ end
97
+
98
+ function Kvol(sz::Matrix, vz::Matrix, rz::Matrix)::Matrix
99
+ sc = π / 180
100
+ eps = acos.(cos.(sc .* sz) .* cos.(sc .* vz) .+ sin.(sc .* sz) .* sin.(sc .* vz) .* cos.(sc .* rz))
101
+ Kvol = 1 ./ (cos.(sc .* sz) .+ cos.(sc .* vz)) .* ((π / 2 .- eps) .* cos.(eps) .+ sin.(eps)) .- π / 4
102
+
103
+ return Kvol
104
+ end
105
+
106
+ function Kvol_vec(sz::AbstractVector, vz::AbstractVector, rz::AbstractVector)
107
+ sc = pi/180
108
+ eps = acos.(cos.(sc .* sz) .* cos.(sc .* vz) .+ sin.(sc .* sz) .* sin.(sc .* vz) .* cos.(sc .* rz))
109
+ Kvol_vec = 1 ./ (cos.(sc .* sz) .+ cos.(sc .* vz)) .* ((pi/2 .- eps) .* cos.(eps) .+ sin.(eps)) .- pi/4
110
+
111
+ return Kvol_vec
112
+ end
113
+
114
+ function Kvol_sc(sz::Real, vz::Real, rz::Real)
115
+ sc = pi/180
116
+ eps = acos(cos(sc*sz)*cos(sc*vz) + sin(sc*sz)*sin(sc*vz)*cos(sc*rz))
117
+ Kvol_sc = 1/(cos(sc*sz) + cos(sc*vz))*((pi/2-eps)*cos(eps) + sin(eps)) - pi/4
118
+
119
+ return Kvol_sc
120
+ end
121
+
122
+ function Kgeo_sc(sz::Real, vz::Real, rz::Real)
123
+ sc = pi/180
124
+ eps = acos(cos(sc*sz)*cos(sc*vz) + sin(sc*sz)*sin(sc*vz)*cos(sc*rz))
125
+ D = sqrt(tan(sc*sz)^2 + tan(sc*vz)^2 - 2*tan(sc*sz)*tan(sc*vz)*cos(sc*rz))
126
+ cost = 2*sqrt(D^2 + (tan(sc*sz)*tan(sc*vz)*sin(sc*rz))^2)/(1/cos(sc*sz) + 1/cos(sc*vz))
127
+ cost = clamp(cost, -1.0, 1.0)
128
+ t = acos(cost)
129
+ O = 1/pi*(t-sin(t)*cos(t))*(1/cos(sc*sz) + 1/cos(sc*vz))
130
+ O = max(O, 0)
131
+ Kgeo_sc = O - 1/cos(sc*sz) - 1/cos(sc*vz) + 0.5*(1+cos(eps))/cos(sc*sz)/cos(sc*vz)
132
+
133
+ return Kgeo_sc
134
+ end
135
+
136
+ function Kgeo(sz::AbstractMatrix, vz::AbstractMatrix, rz::AbstractMatrix)
137
+ n, t = size(sz)
138
+ Kg = zeros(n, t)
139
+ for i in 1:n
140
+ for j in 1:t
141
+ Kg[i,j] = Kgeo_sc(sz[i,j], vz[i,j], rz[i,j])
142
+ end
143
+ end
144
+ return Kg
145
+ end
146
+
147
+ function Kgeo_vec(sz::AbstractVector, vz::AbstractVector, rz::AbstractVector)
148
+ t = length(vz)
149
+ Kg = zeros(t)
150
+
151
+ for j in 1:t
152
+ Kg[j] = Kgeo_sc(sz[j], vz[j], rz[j])
153
+ end
154
+
155
+ return Kg
156
+ end
157
+
158
+ function NRT_BRDF(Y::AbstractMatrix, kv::AbstractMatrix, kg::AbstractMatrix, weighted::Bool, scale::Real)
159
+ n, p = size(Y)
160
+ brdf = fill(NaN, n, 3)
161
+ R2 = fill(NaN, n)
162
+ se = fill(NaN, n)
163
+ uncert = fill(NaN, 3, 3, n)
164
+
165
+ x = ones(3, p)
166
+ for i in 1:n
167
+ yt = Y[i,:]
168
+ non_missing = findall(isfinite.(yt))
169
+ nt = length(non_missing)
170
+
171
+ if nt < 7
172
+ continue
173
+ else
174
+ x[2,:] = kv[i,:]
175
+ x[3,:] = kg[i,:]
176
+
177
+ xt = x[:,non_missing]
178
+ ytt = yt[non_missing]
179
+
180
+ Si = inv(xt * xt')
181
+ brdf[i,:] = (Si * xt * ytt)'
182
+ yp = (brdf[i,:] * xt)'
183
+ uncert[:,:,i] = Si
184
+ se[i] = sqrt(sum((ytt - yp).^2)/(nt-3))
185
+ R2[i] = 1 - se[i]^2 * (nt-3) / var(ytt; corrected=true) / nt
186
+ end
187
+ end
188
+
189
+ return BRDFParameters(brdf, se, R2, uncert)
190
+ end
191
+
192
+ function NRT_BRDF_albedo(Y::AbstractMatrix, sz::AbstractMatrix, vz::AbstractMatrix, rz::AbstractMatrix, soz_noon::AbstractVector, weighted::Bool, scale::Real)
193
+ # RossThick constant
194
+ g0vol = -0.007574
195
+ g1vol = -0.070987
196
+ g2vol = 0.307588
197
+
198
+ # LiSparseR constant
199
+ g0geo = -1.284909
200
+ g1geo = -0.166314
201
+ g2geo = 0.041840
202
+
203
+ gwsa = [1.0, 0.189184, -1.377622]
204
+ gbsa = [1.0, 0.0, 0.0]
205
+
206
+ n, p = size(Y)
207
+ @info "reflectance n: $(n) p: $(p)"
208
+ results = fill(NaN, n, 7) # (wsa, bsa, wsa_se, bsa_se, rmse, R2, nt)
209
+ @info "results shape rows: $(size(results)[1]) cols: $(size(results)[2])"
210
+
211
+ x = ones(3, p)
212
+ for i in 1:n
213
+ yt = Y[i,:]
214
+ non_missing = findall(isfinite.(yt))
215
+ nt = length(non_missing)
216
+
217
+ if nt < 7
218
+ continue
219
+ else
220
+ sznrad = soz_noon[i] * pi/180
221
+ gbsa[2] = g0vol + g1vol * sznrad^2 + g2vol * sznrad^3
222
+ gbsa[3] = g0geo + g1geo * sznrad^2 + g2geo * sznrad^3
223
+
224
+ x[2,:] = Kvol_vec(sz[i,:], vz[i,:], rz[i,:])
225
+ x[3,:] = Kgeo_vec(sz[i,:], vz[i,:], rz[i,:])
226
+
227
+ xt = x[:,non_missing]
228
+ ytt = yt[non_missing]
229
+
230
+ if weighted
231
+ xx = exp.(-0.5 .* range(p-1, stop=0; length=p) ./ scale)
232
+ xxt = xx[non_missing]
233
+
234
+ SSi = Diagonal(xxt)
235
+ Si = inv(xt * SSi * xt')
236
+ brdf = Si * xt * SSi * ytt
237
+ yp = (brdf' * xt)'
238
+ se = sqrt(sum(((ytt - yp) .* xxt).^2)/(nt-3))
239
+ else
240
+ Si = inv(xt * xt')
241
+ brdf = Si * xt * ytt
242
+ yp = (brdf' * xt)'
243
+ se = sqrt(sum((ytt - yp).^2)/(nt-3))
244
+ end
245
+
246
+ results[i,1] = dot(gwsa, brdf) # wsa
247
+ results[i,2] = dot(gbsa, brdf) # bsa
248
+
249
+ results[i,3] = se * sqrt(dot(gwsa, Si * gwsa)) # wsa se
250
+ results[i,4] = se * sqrt(dot(gbsa, Si * gbsa)) # bsa se
251
+
252
+ results[i,5] = se # brdf rmse
253
+ results[i,6] = 1 - se^2 * (nt-3) / var(ytt; corrected=true) / nt # brdf R2
254
+ results[i,7] = nt # number of obs for brdf estimation
255
+ end
256
+ end
257
+
258
+ return results
259
+ end
260
+
261
+ function NRT_BRDF_nadir(Y::AbstractMatrix, sz::AbstractMatrix, vz::AbstractMatrix, rz::AbstractMatrix, soz_noon::AbstractVector, weighted::Bool, scale::Real)
262
+ n, p = size(Y)
263
+ results = fill(NaN, n, 5) # (nadir, nadir_se, rmse, R2, nt)
264
+ x = ones(3, p)
265
+ Knadir = [1.0, 0.0, 0.0]
266
+
267
+ for i in 1:n
268
+ yt = Y[i,:]
269
+ non_missing = findall(isfinite.(yt))
270
+ nt = length(non_missing)
271
+
272
+ if nt < 7
273
+ continue
274
+ else
275
+ szn = soz_noon[i]
276
+ x[2,:] = Kvol_vec(sz[i,:], vz[i,:], rz[i,:])
277
+ x[3,:] = Kgeo_vec(sz[i,:], vz[i,:], rz[i,:])
278
+
279
+ xt = x[:,non_missing]
280
+ ytt = yt[non_missing]
281
+
282
+ if weighted
283
+ xx = exp.(-0.5 .* range(p-1, stop=0; length=p) ./ scale)
284
+ xxt = xx[non_missing]
285
+ SSi = Diagonal(xxt)
286
+ Si = inv(xt * SSi * xt')
287
+ brdf = Si * xt * SSi * ytt
288
+ yp = (brdf' * xt)'
289
+ se = sqrt(sum(((ytt - yp) .* xxt).^2)/(nt-3))
290
+ else
291
+ Si = inv(xt * xt')
292
+ brdf = Si * xt * ytt
293
+ yp = (brdf' * xt)'
294
+ se = sqrt(sum((ytt - yp).^2)/(nt-3))
295
+ end
296
+
297
+ Knadir[2] = Kvol_sc(szn, 0.0, 0.0)
298
+ Knadir[3] = Kgeo_sc(szn, 0.0, 0.0)
299
+ results[i,1] = dot(Knadir, brdf) # nadir
300
+ results[i,2] = se * sqrt(dot(Knadir, Si * Knadir)) # nadir se
301
+ results[i,3] = se # brdf rmse
302
+ results[i,4] = 1 - se^2 * (nt-3) / var(ytt; corrected=true) / nt # brdf R2
303
+ results[i,5] = nt # number of obs for brdf estimation
304
+ end
305
+ end
306
+
307
+ return results
308
+ end
309
+
310
+ function NRT_BRDF_all(Y::AbstractMatrix, sz::AbstractMatrix, vz::AbstractMatrix, rz::AbstractMatrix, soz_noon::AbstractVector, weighted::Bool = true, scale::Real = 1.87)
311
+ # the rows are separate locations and the columns are separate times
312
+ @info "processing BRDF"
313
+ @info "reflectance rows: $(size(Y)[1]) cols: $(size(Y)[2])"
314
+ @info "solar zenith rows: $(size(sz)[1]) cols: $(size(sz)[2])"
315
+ @info "sensor zenith rows: $(size(vz)[1]) cols: $(size(vz)[2])"
316
+ @info "relative azimuth rows: $(size(rz)[1]) cols: $(size(rz)[2])"
317
+ @info "solar zenith noon size: $(size(soz_noon)[1])"
318
+
319
+ # RossThick constants
320
+ g0vol = -0.007574
321
+ g1vol = -0.070987
322
+ g2vol = 0.307588
323
+
324
+ # LiSparseR constants
325
+ g0geo = -1.284909
326
+ g1geo = -0.166314
327
+ g2geo = 0.041840
328
+
329
+ gwsa = [1.0, 0.189184, -1.377622]
330
+ gbsa = [1.0, 0.0, 0.0]
331
+ gnbar = [1.0, 0.0, 0.0]
332
+
333
+ n, p = size(Y)
334
+ @info "n: $(n) p: $(p)"
335
+ results = fill(NaN, n, 9) # (wsa, bsa, nadir, wsa_se, bsa_se, nadir_se, rmse, R2, nt)
336
+ @info "results rows: $(size(results)[1]) cols: $(size(results)[2])"
337
+
338
+ x = ones(3, p)
339
+
340
+ @showprogress for i in 1:n
341
+ yt = Y[i,:]
342
+ non_missing = findall(isfinite.(yt))
343
+ nt = length(non_missing)
344
+
345
+ if nt < 7
346
+ continue
347
+ else
348
+ sznrad = soz_noon[i] * pi/180
349
+ gbsa[2] = g0vol + g1vol * sznrad^2 + g2vol * sznrad^3
350
+ gbsa[3] = g0geo + g1geo * sznrad^2 + g2geo * sznrad^3
351
+
352
+ gnbar[2] = Kvol_sc(soz_noon[i], 0.0, 0.0)
353
+ gnbar[3] = Kgeo_sc(soz_noon[i], 0.0, 0.0)
354
+
355
+ x[2,:] = Kvol_vec(sz[i,:], vz[i,:], rz[i,:])
356
+ x[3,:] = Kgeo_vec(sz[i,:], vz[i,:], rz[i,:])
357
+
358
+ xt = x[:,non_missing]
359
+ ytt = yt[non_missing]
360
+
361
+ if weighted
362
+ xx = exp.(-0.5 .* range(p-1, stop=0; length=p) ./ scale)
363
+ xxt = xx[non_missing]
364
+
365
+ SSi = Diagonal(xxt)
366
+ Si = inv(xt * SSi * xt')
367
+ brdf = Si * xt * SSi * ytt
368
+ yp = (brdf' * xt)'
369
+ se = sqrt(sum(((ytt - yp) .* xxt).^2)/(nt-3))
370
+ else
371
+ Si = inv(xt * xt')
372
+ brdf = Si * xt * ytt
373
+ yp = (brdf' * xt)'
374
+ se = sqrt(sum((ytt - yp).^2)/(nt-3))
375
+ end
376
+
377
+ results[i,1] = dot(gwsa, brdf) # wsa
378
+ results[i,2] = dot(gbsa, brdf) # bsa
379
+ results[i,3] = dot(gnbar, brdf) # nadir
380
+
381
+ results[i,4] = se * sqrt(dot(gwsa, Si * gwsa)) # wsa se
382
+ results[i,5] = se * sqrt(dot(gbsa, Si * gbsa)) # bsa se
383
+ results[i,6] = se * sqrt(dot(gnbar, Si * gnbar)) # nadir se
384
+
385
+ results[i,7] = se # brdf rmse
386
+ results[i,8] = 1 - se^2 * (nt-3) / var(ytt; corrected=true) / nt # brdf R2
387
+ results[i,9] = nt # number of obs for brdf estimation
388
+
389
+ replace!(results, missing=>NaN)
390
+ end
391
+ end
392
+
393
+ return results
394
+ end
395
+
396
+ export NRT_BRDF_all
397
+
398
+ function date_range(start::Date, stop::Date)::Vector{Date}
399
+ return collect(start:Day(1):stop)
400
+ end
401
+
402
+ function calculate_SZA(day_of_year::Array{Int64,1}, hour_of_day::Union{Array{Float64,1}, Float64}, lat::Array{Float64,1})::Array{Float64,1}
403
+ day_angle = (2 * pi * (day_of_year .- 1)) / 365
404
+ lat = deg2rad.(lat)
405
+ dec = deg2rad.((0.006918 .- 0.399912 .* cos.(day_angle) .+ 0.070257 .* sin.(day_angle) .- 0.006758 .* cos.(2 .* day_angle) .+ 0.000907 .* sin.(2 .* day_angle) .- 0.002697 .* cos.(3 .* day_angle) .+ 0.00148 .* sin.(3 .* day_angle)) .* (180 / pi))
406
+ hour_angle = deg2rad.(hour_of_day * 15.0 .- 180.0)
407
+ SZA = rad2deg.(acos.(sin.(lat) .* sin.(dec) .+ cos.(lat) .* cos.(dec) .* cos.(hour_angle)))
408
+ return SZA
409
+ end
410
+
411
+ end
File without changes
@@ -0,0 +1,3 @@
1
+ from .version import __version__, __author__
2
+ from .ECOv003_L2T_STARS import *
3
+ from .ECOv003_DL import *
@@ -0,0 +1,60 @@
1
+ import logging
2
+
3
+ import numpy as np
4
+ from scipy import stats
5
+
6
+ from rasters import Raster
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+ def calibrate_fine_to_coarse(fine_image: Raster, coarse_image: Raster) -> Raster:
11
+ """
12
+ Calibrates a fine-resolution raster image to a coarse-resolution raster image
13
+ using linear regression.
14
+
15
+ This function aggregates the fine image to the geometry of the coarse image,
16
+ then performs a linear regression between the aggregated fine image and the
17
+ original coarse image. The derived slope and intercept are then applied to
18
+ the original fine image for calibration.
19
+
20
+ Args:
21
+ fine_image (Raster): The higher-resolution raster image to be calibrated.
22
+ coarse_image (Raster): The lower-resolution raster image used as the reference
23
+ for calibration.
24
+
25
+ Returns:
26
+ Raster: The calibrated fine-resolution raster image. If too few valid
27
+ data points are available for regression (less than 30), the
28
+ original fine_image is returned.
29
+ """
30
+ # Aggregate the fine image to the coarse image's geometry for comparison
31
+ aggregated_image = fine_image.to_geometry(coarse_image.geometry, resampling="average")
32
+ x = np.array(aggregated_image).flatten() # Independent variable (aggregated fine)
33
+ y = np.array(coarse_image).flatten() # Dependent variable (coarse)
34
+
35
+ # Create a mask to remove NaN values from both arrays, ensuring valid data points for regression
36
+ mask = ~np.isnan(x) & ~np.isnan(y)
37
+
38
+ # Check if there are enough valid data points for a meaningful linear regression
39
+ if np.count_nonzero(mask) < 30:
40
+ logger.warning(
41
+ f"Insufficient valid data points ({np.count_nonzero(mask)}) for calibration. "
42
+ "Returning original fine image."
43
+ )
44
+ return fine_image
45
+
46
+ # Apply the mask to get only valid data points
47
+ x = x[mask]
48
+ y = y[mask]
49
+
50
+ # Perform linear regression
51
+ slope, intercept, r_value, p_value, std_err = stats.linregress(x, y)
52
+ logger.info(
53
+ f"Linear regression for calibration: slope={slope:.4f}, intercept={intercept:.4f}, "
54
+ f"R-squared={r_value**2:.4f}"
55
+ )
56
+
57
+ # Apply the derived calibration to the original fine image
58
+ calibrated_image = fine_image * slope + intercept
59
+
60
+ return calibrated_image
@@ -0,0 +1,38 @@
1
+ from os.path import join, abspath, dirname
2
+
3
+ # Template file for generating the L2T_STARS run configuration XML.
4
+ L2T_STARS_TEMPLATE = join(abspath(dirname(__file__)), "ECOv003_L2T_STARS.xml")
5
+
6
+ # Default directories and parameters for the L2T_STARS processing.
7
+ DEFAULT_WORKING_DIRECTORY = "." # Current directory
8
+ DEFAULT_BUILD = "0700" # Default build ID
9
+ PRIMARY_VARIABLE = "NDVI" # Primary variable of interest
10
+ DEFAULT_OUTPUT_DIRECTORY = "L2T_STARS_output"
11
+ DEFAULT_STARS_SOURCES_DIRECTORY = "L2T_STARS_SOURCES"
12
+ DEFAULT_STARS_INDICES_DIRECTORY = "L2T_STARS_INDICES"
13
+ DEFAULT_STARS_MODEL_DIRECTORY = "L2T_STARS_MODEL"
14
+ DEFAULT_STARS_PRODUCTS_DIRECTORY = "STARS_products"
15
+ DEFAULT_HLS_DOWNLOAD_DIRECTORY = "HLS2_download"
16
+ DEFAULT_LANDSAT_DOWNLOAD_DIRECTORY = "HLS2_download" # Redundant but kept for clarity
17
+ DEFAULT_HLS_PRODUCTS_DIRECTORY = "HLS2_products"
18
+ DEFAULT_VIIRS_DOWNLOAD_DIRECTORY = "VIIRS_download"
19
+ DEFAULT_VIIRS_PRODUCTS_DIRECTORY = "VIIRS_products"
20
+ DEFAUL_VIIRS_MOSAIC_DIRECTORY = "VIIRS_mosaic"
21
+ DEFAULT_GEOS5FP_DOWNLOAD_DIRECTORY = "GEOS5FP_download"
22
+ DEFAULT_GEOS5FP_PRODUCTS_DIRECTORY = "GEOS5FP_products"
23
+ DEFAULT_VNP09GA_PRODUCTS_DIRECTORY = "VNP09GA_products"
24
+ DEFAULT_VNP43NRT_PRODUCTS_DIRECTORY = "VNP43NRT_products"
25
+
26
+ # Processing parameters
27
+ VIIRS_GIVEUP_DAYS = 4 # Number of days to give up waiting for VIIRS data
28
+ DEFAULT_SPINUP_DAYS = 7 # Spin-up period for time-series analysis
29
+ DEFAULT_TARGET_RESOLUTION = 70 # Target output resolution in meters
30
+ DEFAULT_NDVI_RESOLUTION = 490 # NDVI coarse resolution in meters
31
+ DEFAULT_ALBEDO_RESOLUTION = 980 # Albedo coarse resolution in meters
32
+ DEFAULT_USE_SPATIAL = False # Flag for using spatial interpolation (currently unused)
33
+ DEFAULT_USE_VNP43NRT = True # Flag for using VNP43NRT VIIRS product
34
+ DEFAULT_CALIBRATE_FINE = False # Flag for calibrating fine resolution data to coarse
35
+
36
+ # Product short and long names
37
+ L2T_STARS_SHORT_NAME = "ECO_L2T_STARS"
38
+ L2T_STARS_LONG_NAME = "ECOSTRESS Tiled Auxiliary NDVI and Albedo L2 Global 70 m"
@@ -0,0 +1 @@
1
+ from .daterange import *
@@ -0,0 +1,35 @@
1
+ import datetime
2
+ import logging
3
+ from typing import List, Union
4
+
5
+ from dateutil import parser
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ def get_date(dt: Union[datetime.date, datetime.datetime, str]) -> datetime.date or None:
11
+ if dt is None:
12
+ return None
13
+ elif isinstance(dt, datetime.datetime):
14
+ return dt.date()
15
+ elif isinstance(dt, datetime.date):
16
+ return dt
17
+ elif isinstance(dt, str):
18
+ return parser.parse(dt).date()
19
+ else:
20
+ raise ValueError(f"invalid date type: {type(dt)}")
21
+
22
+
23
+ def date_range(start: Union[datetime.date, str], end: Union[datetime.date, str]) -> List[datetime.date]:
24
+ start = get_date(start)
25
+ end = get_date(end)
26
+
27
+ try:
28
+ count = (end - start).days
29
+ except TypeError as e:
30
+ logger.exception(e)
31
+ raise TypeError(f"start type: {type(start)} end type: {type(end)}")
32
+
33
+ dates = [start + datetime.timedelta(days=days) for days in range(count + 1)]
34
+
35
+ return dates