6 from datetime
import datetime, timedelta
11 from shutil
import copyfile
as cp
12 import xml.etree.ElementTree
as ET
18 versionStr =
"1.0 (2023-05-04)"
21 tree = ET.parse(manifestfile)
26 for child
in root.find(
'dataObjectSection'):
27 filename = pathlib.PurePath(child.find(
'byteStream').find(
'fileLocation').attrib[
'href']).name
28 if 'radiance' in filename:
29 radfiles.append(filename)
30 elif 'tie_' in filename:
31 tiefiles.append(filename)
32 elif 'time_coordinates' in filename:
35 engfiles.append(filename)
37 return (radfiles, tiefiles, engfiles)
40 return arr.min(), arr.max()
44 base = datetime(2000, 1, 1, 0, 0, 0)
45 t = base + timedelta(microseconds=
int(usec))
46 return t.strftime(
'%Y-%m-%dT%H:%M:%S.%fZ')
51 north=None, south=None, west=None, east=None,
52 spixl=None, epixl=None, sline=None, eline=None,
55 self.
idir = pathlib.Path(idir)
56 self.
odir = pathlib.Path(odir)
84 for filename
in files:
85 srcfile = self.
idir / filename
87 dstfile = self.
odir / filename
89 print(
'Extracting', srcfile)
94 with netCDF4.Dataset(dstfile, mode=
'a')
as dst:
95 dst.setncatts(self.
attrs)
100 print(
"north={} south={} west={} east={}".
108 pl.lonlat2pixline(zero=
False)
110 (pl.spixl, pl.epixl, pl.sline, pl.eline)
120 self.
odir =
'.'.join([self.
idir,
'subset'])
121 pathlib.Path(self.
odir).mkdir(parents=
True, exist_ok=
True)
124 with netCDF4.Dataset(self.
tiefile,
'r')
as src:
125 npixl = src.dimensions[
'tie_columns'].size
126 nline = src.dimensions[
'tie_rows'].size
127 dpixl = getattr(src,
'ac_subsampling_factor', 1)
128 dline = getattr(src,
'al_subsampling_factor', 1)
129 spixl, epixl = [self.
spixl, self.
epixl + dpixl - 1] // dpixl
130 sline, eline = [self.
sline, self.
eline + dline - 1] // dline
137 pixls_needed = points_required - (epixl - spixl + 1)
139 spixl = max(0, spixl - pixls_needed // 2)
140 epixl = min(spixl + points_required, npixl) - 1
141 if epixl == npixl - 1:
142 spixl = npixl - points_required
144 lines_needed = points_required - (eline - sline + 1)
146 sline = max(0, sline - lines_needed // 2)
147 eline = min(sline + points_required, nline) - 1
148 if eline == nline - 1:
149 sline = nline - points_required
153 spixl, epixl = [dpixl*v
for v
in (spixl, epixl)]
154 sline, eline = [dline*v
for v
in (sline, eline)]
156 print(
"spixl={} epixl={} sline={} eline={}".
157 format(spixl+1, epixl+1, sline+1, eline+1))
160 with netCDF4.Dataset(self.
timefile,
'r')
as src:
161 ts = src[
'time_stamp'][[sline, eline]]
166 with netCDF4.Dataset(self.
geofile,
'r')
as src:
167 lat_min, lat_max =
minmax(src[
'latitude']
168 [sline:eline, spixl:epixl])
169 lon_min, lon_max =
minmax(src[
'longitude']
170 [sline:eline, spixl:epixl])
173 self.
attrs = {
'start_time': start_time,
174 'stop_time': stop_time,
175 'geospatial_lat_min': lat_min,
176 'geospatial_lat_max': lat_max,
177 'geospatial_lon_min': lon_min,
178 'geospatial_lon_max': lon_max }
182 subset = {
'rows': [sline, eline]}
186 subset = {
'columns':[spixl, epixl],
187 'rows': [sline, eline]}
189 status += self.
runextract(radfiles + engfiles, subset)
192 subset = {
'tie_columns':[spixl, epixl] // dpixl,
193 'tie_rows': [sline, eline] // dline}
199 if __name__ ==
"__main__":
200 print(
"l1bextract_safe_nc", versionStr)
203 parser = argparse.ArgumentParser(
204 description=
'Extract specified area from OLCI Level 1B files.',
205 epilog=
'Specify either geographic limits or pixel/line ranges, not both.')
206 parser.add_argument(
'-v',
'--verbose', help=
'print status messages',
208 parser.add_argument(
'idir',
209 help=
'directory containing OLCI Level 1B files')
210 parser.add_argument(
'odir', nargs=
'?',
211 help=
'output directory (defaults to "idir.subset")')
213 group1 = parser.add_argument_group(
'geographic limits')
214 group1.add_argument(
'-n',
'--north', type=float, help=
'northernmost latitude')
215 group1.add_argument(
'-s',
'--south', type=float, help=
'southernmost latitude')
216 group1.add_argument(
'-w',
'--west', type=float, help=
'westernmost longitude')
217 group1.add_argument(
'-e',
'--east', type=float, help=
'easternmost longitude')
219 group2 = parser.add_argument_group(
'pixel/line ranges (1-based)')
220 group2.add_argument(
'--spixl', type=int, help=
'start pixel')
221 group2.add_argument(
'--epixl', type=int, help=
'end pixel')
222 group2.add_argument(
'--sline', type=int, help=
'start line')
223 group2.add_argument(
'--eline', type=int, help=
'end line')
225 if len(sys.argv) == 1:
228 args = parser.parse_args()
241 verbose=args.verbose)
244 if not this.idir.exists():
245 print(
"ERROR: Directory '{}' does not exist. Exiting.".format(this.idir))
247 if not this.timefile.exists():
248 print(
"ERROR: Timestamp file ({}) not found!".format(this.timefile))
250 if not this.geofile.exists():
251 print(
"ERROR: Geolocation file ({}) not found!".format(this.geofile))
253 if not this.tiefile.exists():
254 print(
"ERROR: Tie file ({}) not found!".format(this.tiefile))
258 goodlatlons =
None not in (this.north, this.south, this.west, this.east)
259 goodindices =
None not in (this.spixl, this.epixl, this.sline, this.eline)
260 if (goodlatlons
and goodindices):
261 print(
"ERROR: Specify either geographic limits or pixel/line ranges, not both.")
264 status = this.getpixlin()
265 if status
not in (0, 110):
266 print(
"No extract; lonlat2pixline status =", status)
271 print(
"ERROR: Specify all values for either geographic limits or pixel/line ranges.")
278 cp(this.manifest, this.odir /
'xfdumanifest.xml')