1 /*
2 This file is part of CDO. CDO is a collection of Operators to manipulate and analyse Climate model Data.
3
4 Author: Uwe Schulzweida
5
6 */
7
8 /*
9 This module contains the following operators:
10
11 Selbox sellonlatbox Select lon/lat box
12 Selbox selindexbox Select index box
13 */
14
15 #include <cdi.h>
16
17 #include <mpim_grid.h>
18 #include "grid_define.h"
19 #include "cdo_options.h"
20 #include "process_int.h"
21 #include "cdo_vlist.h"
22 #include "param_conversion.h"
23 #include "gridreference.h"
24 #include "selboxinfo.h"
25
26 static void
correct_xvals(const long nlon,const long inc,double * xvals)27 correct_xvals(const long nlon, const long inc, double *xvals)
28 {
29 if (nlon > 1 && IS_EQUAL(xvals[0], xvals[(nlon - 1) * inc])) xvals[(nlon - 1) * inc] += 360;
30
31 if (xvals[0] > xvals[(nlon - 1) * inc])
32 for (long i = 0; i < nlon; i++)
33 if (xvals[i * inc] >= 180) xvals[i * inc] -= 360;
34
35 for (long i = 0; i < nlon; i++)
36 {
37 if (xvals[i * inc] < -180) xvals[i * inc] += 360;
38 if (xvals[i * inc] > 360) xvals[i * inc] -= 360;
39 }
40
41 if (xvals[0] > xvals[(nlon - 1) * inc])
42 for (long i = 1; i < nlon; i++)
43 if (xvals[i * inc] < xvals[(i - 1) * inc]) xvals[i * inc] += 360;
44 }
45
46 static void
gengridxyvals(const int gridtype,const int gridID1,const int gridID2,const long nlon,const long nlat,const long nlon2,const long nlat2,const SelboxInfo & sbox,const bool unitsIsDegree)47 gengridxyvals(const int gridtype, const int gridID1, const int gridID2, const long nlon, const long nlat, const long nlon2,
48 const long nlat2, const SelboxInfo &sbox, const bool unitsIsDegree)
49 {
50 Varray<double> xvals1, yvals1, xvals2, yvals2;
51
52 const bool lxvals = gridInqXvals(gridID1, nullptr) > 0;
53 const bool lyvals = gridInqYvals(gridID1, nullptr) > 0;
54
55 if (gridtype == GRID_CURVILINEAR)
56 {
57 if (lxvals && lyvals)
58 {
59 xvals1.resize(nlon * nlat);
60 yvals1.resize(nlon * nlat);
61 xvals2.resize(nlon2 * nlat2);
62 yvals2.resize(nlon2 * nlat2);
63 }
64 }
65 else
66 {
67 if (lxvals) xvals1.resize(nlon);
68 if (lyvals) yvals1.resize(nlat);
69 if (lxvals) xvals2.resize(nlon2);
70 if (lyvals) yvals2.resize(nlat2);
71 }
72
73 auto pxvals2 = xvals2.data();
74 auto pyvals2 = yvals2.data();
75
76 if (lxvals) gridInqXvals(gridID1, xvals1.data());
77 if (lyvals) gridInqYvals(gridID1, yvals1.data());
78
79 const auto &lat1 = sbox.lat1;
80 const auto &lat2 = sbox.lat2;
81 const auto &lon11 = sbox.lon11;
82 const auto &lon12 = sbox.lon12;
83 const auto &lon21 = sbox.lon21;
84 const auto &lon22 = sbox.lon22;
85
86 if (gridtype == GRID_CURVILINEAR)
87 {
88 if (lxvals && lyvals)
89 for (long ilat = lat1; ilat <= lat2; ilat++)
90 {
91 for (long ilon = lon21; ilon <= lon22; ilon++)
92 {
93 *pxvals2++ = xvals1[ilat * nlon + ilon];
94 *pyvals2++ = yvals1[ilat * nlon + ilon];
95 }
96 for (long ilon = lon11; ilon <= lon12; ilon++)
97 {
98 *pxvals2++ = xvals1[ilat * nlon + ilon];
99 *pyvals2++ = yvals1[ilat * nlon + ilon];
100 }
101 }
102 }
103 else
104 {
105 if (lxvals)
106 {
107 for (long i = lon21; i <= lon22; i++) *pxvals2++ = xvals1[i];
108 for (long i = lon11; i <= lon12; i++) *pxvals2++ = xvals1[i];
109 if (unitsIsDegree) correct_xvals(nlon2, 1, xvals2.data());
110 }
111
112 if (lyvals)
113 for (long i = lat1; i <= lat2; i++) *pyvals2++ = yvals1[i];
114 }
115 // for ( int i = 0; i < nlat2; i++ ) printf("lat : %d %g\n", i+1, yvals2[i]);
116 // for ( int i = 0; i < nlon2; i++ ) printf("lon : %d %g\n", i+1, xvals2[i]);
117 if (lxvals) gridDefXvals(gridID2, xvals2.data());
118 if (lyvals) gridDefYvals(gridID2, yvals2.data());
119 }
120
121 static void
gengridXboundsCurvi(const int gridID1,const long nlon,const long nlat,const long nlon2,const long nlat2,const SelboxInfo & sbox,Varray<double> & xbounds1,Varray<double> & xbounds2)122 gengridXboundsCurvi(const int gridID1, const long nlon, const long nlat, const long nlon2, const long nlat2, const SelboxInfo &sbox,
123 Varray<double> &xbounds1, Varray<double> &xbounds2)
124 {
125 xbounds1.resize(4 * nlon * nlat);
126 xbounds2.resize(4 * nlon2 * nlat2);
127
128 auto pxbounds2 = xbounds2.data();
129
130 gridInqXbounds(gridID1, xbounds1.data());
131
132 const auto &lat1 = sbox.lat1;
133 const auto &lat2 = sbox.lat2;
134 const auto &lon11 = sbox.lon11;
135 const auto &lon12 = sbox.lon12;
136 const auto &lon21 = sbox.lon21;
137 const auto &lon22 = sbox.lon22;
138 for (long ilat = lat1; ilat <= lat2; ilat++)
139 {
140 for (long ilon = 4 * lon21; ilon < 4 * (lon22 + 1); ilon++) *pxbounds2++ = xbounds1[4 * ilat * nlon + ilon];
141
142 for (long ilon = 4 * lon11; ilon < 4 * (lon12 + 1); ilon++) *pxbounds2++ = xbounds1[4 * ilat * nlon + ilon];
143 }
144 }
145
146 static void
gengridYboundsCurvi(const int gridID1,const long nlon,const long nlat,const long nlon2,const long nlat2,const SelboxInfo & sbox,Varray<double> & ybounds1,Varray<double> & ybounds2)147 gengridYboundsCurvi(const int gridID1, const long nlon, const long nlat, const long nlon2, const long nlat2, const SelboxInfo &sbox,
148 Varray<double> &ybounds1, Varray<double> &ybounds2)
149 {
150 ybounds1.resize(4 * nlon * nlat);
151 ybounds2.resize(4 * nlon2 * nlat2);
152
153 auto pybounds2 = ybounds2.data();
154
155 gridInqYbounds(gridID1, ybounds1.data());
156
157 const auto &lat1 = sbox.lat1;
158 const auto &lat2 = sbox.lat2;
159 const auto &lon11 = sbox.lon11;
160 const auto &lon12 = sbox.lon12;
161 const auto &lon21 = sbox.lon21;
162 const auto &lon22 = sbox.lon22;
163 for (long ilat = lat1; ilat <= lat2; ilat++)
164 {
165 for (long ilon = 4 * lon21; ilon < 4 * (lon22 + 1); ilon++) *pybounds2++ = ybounds1[4 * ilat * nlon + ilon];
166
167 for (long ilon = 4 * lon11; ilon < 4 * (lon12 + 1); ilon++) *pybounds2++ = ybounds1[4 * ilat * nlon + ilon];
168 }
169 }
170
171 static void
gengridXboundsRect2D(const int gridID1,const long nlon,const long nlon2,const SelboxInfo & sbox,const bool unitsIsDegree,Varray<double> & xbounds1,Varray<double> & xbounds2)172 gengridXboundsRect2D(const int gridID1, const long nlon, const long nlon2, const SelboxInfo &sbox, const bool unitsIsDegree,
173 Varray<double> &xbounds1, Varray<double> &xbounds2)
174 {
175 xbounds1.resize(2 * nlon);
176 xbounds2.resize(2 * nlon2);
177
178 auto pxbounds2 = xbounds2.data();
179
180 gridInqXbounds(gridID1, xbounds1.data());
181
182 const auto &lon11 = sbox.lon11;
183 const auto &lon12 = sbox.lon12;
184 const auto &lon21 = sbox.lon21;
185 const auto &lon22 = sbox.lon22;
186 for (long i = 2 * lon21; i < 2 * (lon22 + 1); i++) *pxbounds2++ = xbounds1[i];
187 for (long i = 2 * lon11; i < 2 * (lon12 + 1); i++) *pxbounds2++ = xbounds1[i];
188
189 if (unitsIsDegree)
190 {
191 correct_xvals(nlon2, 2, xbounds2.data());
192 correct_xvals(nlon2, 2, xbounds2.data() + 1);
193 // make sure that the first bound is less than the second
194 long nx = 0;
195 for (long i = 0; i < nlon2; ++i)
196 if (xbounds2[2 * i] > xbounds2[2 * i + 1]) nx++;
197 if (nx == nlon2 && xbounds2[0] > -180.)
198 for (long i = 0; i < nlon2; ++i) xbounds2[2 * i] -= 360.;
199 }
200 }
201
202 static void
gengridYboundsRect2D(const int gridID1,const long nlat,const long nlat2,const SelboxInfo & sbox,Varray<double> & ybounds1,Varray<double> & ybounds2)203 gengridYboundsRect2D(const int gridID1, const long nlat, const long nlat2, const SelboxInfo &sbox, Varray<double> &ybounds1,
204 Varray<double> &ybounds2)
205 {
206 ybounds1.resize(2 * nlat);
207 ybounds2.resize(2 * nlat2);
208
209 auto pybounds2 = ybounds2.data();
210
211 gridInqYbounds(gridID1, ybounds1.data());
212
213 const auto &lat1 = sbox.lat1;
214 const auto &lat2 = sbox.lat2;
215 for (long i = 2 * lat1; i < 2 * (lat2 + 1); i++) *pybounds2++ = ybounds1[i];
216 }
217
218 static void
gengridXbounds(const int gridID1,const int gridID2,const long nlon,const long nlat,const long nlon2,const long nlat2,const SelboxInfo & sbox,const bool unitsIsDegree)219 gengridXbounds(const int gridID1, const int gridID2, const long nlon, const long nlat, const long nlon2, const long nlat2,
220 const SelboxInfo &sbox, const bool unitsIsDegree)
221 {
222 Varray<double> xbounds1, xbounds2;
223 const auto gridtype = gridInqType(gridID1);
224
225 if (gridtype == GRID_CURVILINEAR)
226 {
227 gridDefNvertex(gridID2, 4);
228 gengridXboundsCurvi(gridID1, nlon, nlat, nlon2, nlat2, sbox, xbounds1, xbounds2);
229 }
230 else
231 {
232 gridDefNvertex(gridID2, 2);
233 gengridXboundsRect2D(gridID1, nlon, nlon2, sbox, unitsIsDegree, xbounds1, xbounds2);
234 }
235
236 gridDefXbounds(gridID2, xbounds2.data());
237 }
238
239 static void
gengridYbounds(const int gridID1,const int gridID2,const long nlon,const long nlat,const long nlon2,const long nlat2,const SelboxInfo & sbox)240 gengridYbounds(const int gridID1, const int gridID2, const long nlon, const long nlat, const long nlon2, const long nlat2,
241 const SelboxInfo &sbox)
242 {
243 Varray<double> ybounds1, ybounds2;
244 const auto gridtype = gridInqType(gridID1);
245
246 if (gridtype == GRID_CURVILINEAR)
247 {
248 gridDefNvertex(gridID2, 4);
249 gengridYboundsCurvi(gridID1, nlon, nlat, nlon2, nlat2, sbox, ybounds1, ybounds2);
250 }
251 else
252 {
253 gridDefNvertex(gridID2, 2);
254 gengridYboundsRect2D(gridID1, nlat, nlat2, sbox, ybounds1, ybounds2);
255 }
256
257 gridDefYbounds(gridID2, ybounds2.data());
258 }
259
260 static int
gengrid(const int gridID1,const SelboxInfo & sbox)261 gengrid(const int gridID1, const SelboxInfo &sbox)
262 {
263 const auto &lat1 = sbox.lat1;
264 const auto &lat2 = sbox.lat2;
265 const auto &lon11 = sbox.lon11;
266 const auto &lon12 = sbox.lon12;
267 const auto &lon21 = sbox.lon21;
268 const auto &lon22 = sbox.lon22;
269 const long nlon = gridInqXsize(gridID1);
270 const long nlat = gridInqYsize(gridID1);
271
272 const long nlon21 = sbox.lon12 - sbox.lon11 + 1;
273 const long nlon22 = sbox.lon22 - sbox.lon21 + 1;
274 const long nlon2 = nlon21 + nlon22;
275 const long nlat2 = sbox.lat2 - sbox.lat1 + 1;
276
277 if (Options::cdoVerbose) cdo_print("nlon1=%ld nlat1=%ld", nlon, nlat);
278 if (Options::cdoVerbose) cdo_print("nlon2=%ld nlat2=%ld", nlon2, nlat2);
279
280 const auto gridtype = gridInqType(gridID1);
281
282 const auto gridID2 = gridCreate(gridtype, nlon2 * nlat2);
283 if (nlon > 0) gridDefXsize(gridID2, nlon2);
284 if (nlat > 0) gridDefYsize(gridID2, nlat2);
285 if (nlat > 0) gridDefNP(gridID2, gridInqNP(gridID1));
286
287 int datatype = CDI_UNDEFID;
288 cdiInqKeyInt(gridID1, CDI_GLOBAL, CDI_KEY_DATATYPE, &datatype);
289 cdiDefKeyInt(gridID2, CDI_GLOBAL, CDI_KEY_DATATYPE, datatype);
290
291 grid_copy_keys(gridID1, gridID2);
292
293 if (gridtype == GRID_PROJECTION) grid_copy_mapping(gridID1, gridID2);
294
295 char xunits[CDI_MAX_NAME];
296 int length = CDI_MAX_NAME;
297 cdiInqKeyString(gridID1, CDI_XAXIS, CDI_KEY_UNITS, xunits, &length);
298
299 const bool unitsIsDegree = strncmp(xunits, "degree", 6) == 0;
300
301 gengridxyvals(gridtype, gridID1, gridID2, nlon, nlat, nlon2, nlat2, sbox, unitsIsDegree);
302
303 if (gridInqXbounds(gridID1, nullptr)) gengridXbounds(gridID1, gridID2, nlon, nlat, nlon2, nlat2, sbox, unitsIsDegree);
304 if (gridInqYbounds(gridID1, nullptr)) gengridYbounds(gridID1, gridID2, nlon, nlat, nlon2, nlat2, sbox);
305
306 if (gridtype == GRID_CURVILINEAR && gridHasArea(gridID1))
307 {
308 Varray<double> areaIn(nlon * nlat), areaOut(nlon2 * nlat2);
309 gridInqArea(gridID1, areaIn.data());
310 auto pareaOut = areaOut.data();
311
312 for (long ilat = lat1; ilat <= lat2; ilat++)
313 {
314 for (long ilon = lon21; ilon < (lon22 + 1); ilon++) *pareaOut++ = areaIn[ilat * nlon + ilon];
315 for (long ilon = lon11; ilon < (lon12 + 1); ilon++) *pareaOut++ = areaIn[ilat * nlon + ilon];
316 }
317
318 gridDefArea(gridID2, areaOut.data());
319 }
320
321 const auto projID1 = gridInqProj(gridID1);
322 if (projID1 != CDI_UNDEFID && gridInqType(projID1) == GRID_PROJECTION)
323 {
324 const auto projID2 = gridCreate(GRID_PROJECTION, nlon2 * nlat2);
325 gridDefXsize(projID2, nlon2);
326 gridDefYsize(projID2, nlat2);
327
328 grid_copy_keys(projID1, projID2);
329 grid_copy_mapping(projID1, projID2);
330
331 gengridxyvals(GRID_PROJECTION, projID1, projID2, nlon, nlat, nlon2, nlat2, sbox, false);
332
333 gridDefProj(gridID2, projID2);
334 }
335
336 return gridID2;
337 }
338
339 int
gengridcell(const int gridID1,const size_t gridsize2,const std::vector<long> & cellidx)340 gengridcell(const int gridID1, const size_t gridsize2, const std::vector<long> &cellidx)
341 {
342 int gridID2 = -1;
343 auto gridtype = gridInqType(gridID1);
344 const auto gridsize1 = gridInqSize(gridID1);
345
346 const auto setDimName = (gridtype == GRID_CURVILINEAR);
347 if (gridtype == GRID_CURVILINEAR) gridtype = GRID_UNSTRUCTURED;
348
349 if (gridtype == GRID_UNSTRUCTURED || gridtype == GRID_GENERIC)
350 gridID2 = gridCreate(gridtype, gridsize2);
351 else
352 return gridID2;
353
354 int datatype = CDI_UNDEFID;
355 cdiInqKeyInt(gridID1, CDI_GLOBAL, CDI_KEY_DATATYPE, &datatype);
356 cdiDefKeyInt(gridID2, CDI_GLOBAL, CDI_KEY_DATATYPE, datatype);
357
358 grid_copy_keys(gridID1, gridID2);
359
360 if (setDimName) cdiDefKeyString(gridID2, CDI_XAXIS, CDI_KEY_DIMNAME, "ncells");
361
362 if (gridHasCoordinates(gridID1))
363 {
364 Varray<double> xvals1(gridsize1), yvals1(gridsize1);
365 Varray<double> xvals2(gridsize2), yvals2(gridsize2);
366
367 gridInqXvals(gridID1, xvals1.data());
368 gridInqYvals(gridID1, yvals1.data());
369
370 for (size_t i = 0; i < gridsize2; ++i)
371 {
372 xvals2[i] = xvals1[cellidx[i]];
373 yvals2[i] = yvals1[cellidx[i]];
374 }
375
376 gridDefXvals(gridID2, xvals2.data());
377 gridDefYvals(gridID2, yvals2.data());
378 }
379
380 if (gridHasBounds(gridID1))
381 {
382 const auto nv = gridInqNvertex(gridID1);
383
384 Varray<double> xbounds1(nv * gridsize1), ybounds1(nv * gridsize1);
385 Varray<double> xbounds2(nv * gridsize2), ybounds2(nv * gridsize2);
386
387 gridInqXbounds(gridID1, xbounds1.data());
388 gridInqYbounds(gridID1, ybounds1.data());
389
390 gridDefNvertex(gridID2, nv);
391
392 for (size_t i = 0; i < gridsize2; ++i)
393 {
394 for (int k = 0; k < nv; ++k)
395 {
396 xbounds2[i * nv + k] = xbounds1[cellidx[i] * nv + k];
397 ybounds2[i * nv + k] = ybounds1[cellidx[i] * nv + k];
398 }
399 }
400
401 gridDefXbounds(gridID2, xbounds2.data());
402 gridDefYbounds(gridID2, ybounds2.data());
403 }
404
405 if (gridHasArea(gridID1))
406 {
407 Varray<double> areaIn(gridsize1), areaOut(gridsize2);
408 gridInqArea(gridID1, areaIn.data());
409
410 for (size_t i = 0; i < gridsize2; ++i) areaOut[i] = areaIn[cellidx[i]];
411
412 gridDefArea(gridID2, areaOut.data());
413 }
414
415 return gridID2;
416 }
417
418 void
genlonlatboxReg2d(int gridID,double xlon1,double xlon2,double xlat1,double xlat2,SelboxInfo & sbox)419 genlonlatboxReg2d(int gridID, double xlon1, double xlon2, double xlat1, double xlat2, SelboxInfo &sbox)
420 {
421 auto &lat1 = sbox.lat1;
422 auto &lat2 = sbox.lat2;
423 auto &lon11 = sbox.lon11;
424 auto &lon12 = sbox.lon12;
425 auto &lon21 = sbox.lon21;
426 auto &lon22 = sbox.lon22;
427 lat1 = 0;
428 lat2 = 0;
429 lon11 = 1;
430 lon12 = 0;
431 lon21 = 0;
432 lon22 = 0;
433
434 if (IS_NOT_EQUAL(xlon1, xlon2))
435 {
436 xlon2 -= 360 * std::floor((xlon2 - xlon1) / 360);
437 if (IS_EQUAL(xlon1, xlon2)) xlon2 += 360;
438 }
439 else
440 {
441 xlon2 += 0.00001;
442 }
443
444 const long nlon = gridInqXsize(gridID);
445 const long nlat = gridInqYsize(gridID);
446
447 Varray<double> xvals(nlon), yvals(nlat);
448
449 if (nlon > 0)
450 {
451 gridInqXvals(gridID, xvals.data());
452
453 cdo_grid_to_degree(gridID, CDI_XAXIS, nlon, xvals.data(), "grid center lon");
454
455 xlon2 -= 360 * std::floor((xlon1 - xvals[0]) / 360);
456 xlon1 -= 360 * std::floor((xlon1 - xvals[0]) / 360);
457 }
458
459 if (nlat > 0)
460 {
461 gridInqYvals(gridID, yvals.data());
462
463 cdo_grid_to_degree(gridID, CDI_YAXIS, nlat, yvals.data(), "grid center lat");
464 }
465
466 if (nlon > 0)
467 {
468 // while ( nlon == 1 || (xvals[nlon-1] - xvals[0]) >= 360 ) nlon--;
469
470 for (lon21 = 0; lon21 < nlon && xvals[lon21] < xlon1; lon21++)
471 ;
472 for (lon22 = lon21; lon22 < nlon && xvals[lon22] < xlon2; lon22++)
473 ;
474
475 if (lon22 >= nlon || xvals[lon22] > xlon2) lon22--;
476
477 xlon1 -= 360;
478 xlon2 -= 360;
479
480 for (lon11 = 0; xvals[lon11] < xlon1; lon11++)
481 ;
482 for (lon12 = lon11; lon12 < nlon && xvals[lon12] < xlon2; lon12++)
483 ;
484
485 // lon12--;
486 if (lon12 >= nlon || xvals[lon12] > xlon2) lon12--;
487 if (lon12 >= 0 && IS_EQUAL(xvals[lon12], xvals[lon21])) lon12--;
488
489 if (lon12 - lon11 + 1 + lon22 - lon21 + 1 <= 0) cdo_abort("Longitudinal dimension is too small!");
490 }
491
492 if (nlat > 0)
493 {
494 if (yvals[0] > yvals[nlat - 1])
495 {
496 if (xlat1 > xlat2)
497 {
498 for (lat1 = 0; lat1 < nlat && yvals[lat1] > xlat1; lat1++)
499 ;
500 for (lat2 = nlat - 1; lat2 && yvals[lat2] < xlat2; lat2--)
501 ;
502 }
503 else
504 {
505 for (lat1 = 0; lat1 < nlat && yvals[lat1] > xlat2; lat1++)
506 ;
507 for (lat2 = nlat - 1; lat2 && yvals[lat2] < xlat1; lat2--)
508 ;
509 }
510 }
511 else
512 {
513 if (xlat1 < xlat2)
514 {
515 for (lat1 = 0; lat1 < nlat && yvals[lat1] < xlat1; lat1++)
516 ;
517 for (lat2 = nlat - 1; lat2 && yvals[lat2] > xlat2; lat2--)
518 ;
519 }
520 else
521 {
522 for (lat1 = 0; lat1 < nlat && yvals[lat1] < xlat2; lat1++)
523 ;
524 for (lat2 = nlat - 1; lat2 && yvals[lat2] > xlat1; lat2--)
525 ;
526 }
527 }
528
529 if (lat2 - lat1 + 1 <= 0) cdo_abort("Latitudinal dimension is too small!");
530 }
531 }
532
533 static void
genlonlatboxCurv(const int gridID,double xlon1,double xlon2,double xlat1,double xlat2,SelboxInfo & sbox)534 genlonlatboxCurv(const int gridID, double xlon1, double xlon2, double xlat1, double xlat2, SelboxInfo &sbox)
535 {
536 auto &lat1 = sbox.lat1;
537 auto &lat2 = sbox.lat2;
538 auto &lon11 = sbox.lon11;
539 auto &lon12 = sbox.lon12;
540 auto &lon21 = sbox.lon21;
541 auto &lon22 = sbox.lon22;
542 const long nlon = gridInqXsize(gridID);
543 const long nlat = gridInqYsize(gridID);
544 const size_t gridsize = nlon * nlat;
545
546 const auto grid_is_circular = gridIsCircular(gridID);
547
548 Varray<double> xvals(gridsize), yvals(gridsize);
549
550 gridInqXvals(gridID, xvals.data());
551 gridInqYvals(gridID, yvals.data());
552
553 // Convert lat/lon units if required
554 cdo_grid_to_degree(gridID, CDI_XAXIS, gridsize, xvals.data(), "grid center lon");
555 cdo_grid_to_degree(gridID, CDI_YAXIS, gridsize, yvals.data(), "grid center lat");
556
557 if (xlon1 > xlon2) cdo_abort("The second longitude have to be greater than the first one!");
558 /*
559 if ( xlon2 < 180. )
560 {
561 xlon1 += 360;
562 xlon2 += 360;
563 }
564 */
565 if (xlat1 > xlat2) std::swap(xlat1, xlat2);
566
567 lat1 = nlat - 1;
568 lat2 = 0;
569 lon11 = 0;
570 lon12 = -1;
571 // lon11 = nlon-1;
572 // lon12 = 0;
573 lon21 = nlon - 1;
574 lon22 = 0;
575
576 bool lp2 = false;
577 double xfirst, xlast, ylast;
578
579 if (grid_is_circular)
580 {
581 for (long ilat = 0; ilat < nlat; ilat++)
582 {
583 xlast = xvals[ilat * nlon + nlon - 1];
584 ylast = yvals[ilat * nlon + nlon - 1];
585 if (ylast >= xlat1 && ylast <= xlat2)
586 if (xlon1 <= xlast && xlon2 > xlast && (xlon2 - xlon1) < 360)
587 {
588 lon11 = nlon - 1;
589 lon12 = 0;
590 lp2 = true;
591 break;
592 }
593 }
594 }
595
596 for (long ilat = 0; ilat < nlat; ilat++)
597 for (long ilon = 0; ilon < nlon; ilon++)
598 {
599 const auto xval = xvals[ilat * nlon + ilon];
600 const auto yval = yvals[ilat * nlon + ilon];
601 if (yval >= xlat1 && yval <= xlat2)
602 {
603 if (lp2)
604 {
605 xfirst = xvals[ilat * nlon];
606 if (xfirst < xlon1) xfirst = xlon1;
607
608 xlast = xvals[ilat * nlon + nlon - 1];
609 if (xlast > xlon2) xlast = xlon2;
610
611 // printf("%g %g %g %g %g %g\n", yval, xval, xlon1, xlast, xfirst, xlon2);
612 if (xval >= xlon1 && xval <= xlast)
613 {
614 if (ilon < lon21) lon21 = ilon;
615 if (ilon > lon22) lon22 = ilon;
616 if (ilat < lat1) lat1 = ilat;
617 if (ilat > lat2) lat2 = ilat;
618 }
619 else if (xval >= xfirst && xval <= xlon2)
620 {
621 if (ilon < lon11) lon11 = ilon;
622 if (ilon > lon12) lon12 = ilon;
623 if (ilat < lat1) lat1 = ilat;
624 if (ilat > lat2) lat2 = ilat;
625 }
626 }
627 else
628 {
629 if (((xval >= xlon1 && xval <= xlon2) || (xval - 360 >= xlon1 && xval - 360 <= xlon2)
630 || (xval + 360 >= xlon1 && xval + 360 <= xlon2)))
631 {
632 if (ilon < lon21) lon21 = ilon;
633 if (ilon > lon22) lon22 = ilon;
634 if (ilat < lat1) lat1 = ilat;
635 if (ilat > lat2) lat2 = ilat;
636 }
637 }
638 /*
639 if ( xval >= xlon1 && xval <= xlon2 )
640 {
641 if ( ilon < lon21 ) lon21 = ilon;
642 if ( ilon > lon22 ) lon22 = ilon;
643 if ( ilat < lat1 ) lat1 = ilat;
644 if ( ilat > lat2 ) lat2 = ilat;
645 }
646 else if ( xval >= xlon1-360 && xval <= xlon2-360 )
647 {
648 if ( ilon < lon11 ) lon11 = ilon;
649 if ( ilon > lon12 ) lon12 = ilon;
650 if ( ilat < lat1 ) lat1 = ilat;
651 if ( ilat > lat2 ) lat2 = ilat;
652 }
653 */
654 }
655 }
656
657 // while ( lon12 >= lon21 ) lon12--;
658 // if ( lon12 <= lon11 ) { lon11 = nlon-1; lon12 = 0; }
659 if (lon12 == 0 && lon11 > 0)
660 {
661 lon11 = 0;
662 lon12 = -1;
663 }
664 /*
665 printf("lon21, lon22, lon11, lon12 idx: %ld %ld %ld %ld lon: %g %g %g %g\n",
666 lon21, lon22, lon11, lon12, xvals[lon21], xvals[lon22], xvals[lon11], xvals[lon12]);
667 */
668 if (lat2 - lat1 + 1 <= 0) cdo_abort("Latitudinal dimension is too small!");
669 }
670
671 void
getlonlatparams(const int argc_offset,double & xlon1,double & xlon2,double & xlat1,double & xlat2)672 getlonlatparams(const int argc_offset, double &xlon1, double &xlon2, double &xlat1, double &xlat2)
673 {
674 bool lset = false;
675
676 const auto nargc = cdo_operator_argc() - argc_offset;
677 if (nargc == 1)
678 {
679 const auto gridname = cdo_operator_argv(argc_offset + 0).c_str();
680 if (strcmp(gridname, "europe") == 0)
681 {
682 xlon1 = -30;
683 xlon2 = 60;
684 xlat1 = 30;
685 xlat2 = 80;
686 lset = true;
687 }
688 }
689
690 if (!lset)
691 {
692 operator_check_argc(argc_offset + 4);
693
694 xlon1 = parameter_to_double(cdo_operator_argv(argc_offset + 0));
695 xlon2 = parameter_to_double(cdo_operator_argv(argc_offset + 1));
696 xlat1 = parameter_to_double(cdo_operator_argv(argc_offset + 2));
697 xlat2 = parameter_to_double(cdo_operator_argv(argc_offset + 3));
698 }
699 }
700
701 void
genlonlatbox(const int argc_offset,const int gridID,SelboxInfo & sbox)702 genlonlatbox(const int argc_offset, const int gridID, SelboxInfo &sbox)
703 {
704 double xlon1 = 0, xlon2 = 0, xlat1 = 0, xlat2 = 0;
705 getlonlatparams(argc_offset, xlon1, xlon2, xlat1, xlat2);
706
707 const auto gridtype = gridInqType(gridID);
708
709 if (gridtype == GRID_CURVILINEAR)
710 genlonlatboxCurv(gridID, xlon1, xlon2, xlat1, xlat2, sbox);
711 else
712 genlonlatboxReg2d(gridID, xlon1, xlon2, xlat1, xlat2, sbox);
713 }
714
715 static int
genlonlatgrid(const int gridID1,SelboxInfo & sbox)716 genlonlatgrid(const int gridID1, SelboxInfo &sbox)
717 {
718 genlonlatbox(0, gridID1, sbox);
719
720 return gengrid(gridID1, sbox);
721 }
722
723 static int
gencellgrid(int gridID1,long & gridsize2,std::vector<long> & cellidx)724 gencellgrid(int gridID1, long &gridsize2, std::vector<long> &cellidx)
725 {
726 const int argc_offset = 0;
727 operator_check_argc(argc_offset + 4);
728
729 auto xlon1 = parameter_to_double(cdo_operator_argv(argc_offset + 0));
730 auto xlon2 = parameter_to_double(cdo_operator_argv(argc_offset + 1));
731 auto xlat1 = parameter_to_double(cdo_operator_argv(argc_offset + 2));
732 auto xlat2 = parameter_to_double(cdo_operator_argv(argc_offset + 3));
733
734 if (xlon1 >= xlon2) std::swap(xlon1, xlon2);
735 if (xlat1 >= xlat2) std::swap(xlat1, xlat2);
736
737 const auto gridtype = gridInqType(gridID1);
738 const auto gridsize1 = gridInqSize(gridID1);
739
740 if (gridtype != GRID_UNSTRUCTURED) cdo_abort("Internal problem, wrong grid type!");
741
742 if (!gridHasCoordinates(gridID1))
743 {
744 const auto reference = dereferenceGrid(gridID1);
745 if (reference.isValid) gridID1 = reference.gridID;
746 if (reference.notFound) cdo_abort("Reference to source grid not found!");
747 }
748
749 if (!gridHasCoordinates(gridID1)) cdo_abort("Grid cell coordinates missing!");
750
751 Varray<double> xvals(gridsize1), yvals(gridsize1);
752 gridInqXvals(gridID1, xvals.data());
753 gridInqYvals(gridID1, yvals.data());
754
755 // Convert lat/lon units if required
756 cdo_grid_to_degree(gridID1, CDI_XAXIS, gridsize1, xvals.data(), "grid center lon");
757 cdo_grid_to_degree(gridID1, CDI_YAXIS, gridsize1, yvals.data(), "grid center lat");
758
759 // find gridsize2
760 long maxcell = 0;
761 long nvals = 0;
762 for (size_t i = 0; i < gridsize1; ++i)
763 {
764 const auto xval = xvals[i];
765 const auto yval = yvals[i];
766 if (yval >= xlat1 && yval <= xlat2)
767 if ((xval >= xlon1 && xval <= xlon2) || (xval + 360 >= xlon1 && xval + 360 <= xlon2)
768 || (xval - 360 >= xlon1 && xval - 360 <= xlon2))
769 {
770 nvals++;
771 if (nvals > maxcell)
772 {
773 constexpr long cellinc = 4096;
774 maxcell += cellinc;
775 cellidx.resize(maxcell);
776 }
777 cellidx[nvals - 1] = i;
778 }
779 }
780
781 if (nvals == 0) cdo_abort("No grid points found!");
782
783 gridsize2 = nvals;
784
785 return gengridcell(gridID1, gridsize2, cellidx);
786 }
787
788 static void
checkBounds(const char * txt,const long minVal,const long maxVal,long & lval)789 checkBounds(const char *txt, const long minVal, const long maxVal, long &lval)
790 {
791 if (lval < minVal)
792 {
793 cdo_warning("%s index out of range, set to %ld!", txt, minVal);
794 lval = minVal;
795 }
796
797 if (lval > maxVal)
798 {
799 cdo_warning("%s index out of range, set to %ld!", txt, maxVal);
800 lval = maxVal;
801 }
802 }
803
804 void
genindexbox(const int argc_offset,const int gridID1,SelboxInfo & sbox)805 genindexbox(const int argc_offset, const int gridID1, SelboxInfo &sbox)
806 {
807 auto &lat1 = sbox.lat1;
808 auto &lat2 = sbox.lat2;
809 auto &lon11 = sbox.lon11;
810 auto &lon12 = sbox.lon12;
811 auto &lon21 = sbox.lon21;
812 auto &lon22 = sbox.lon22;
813
814 operator_check_argc(argc_offset + 4);
815
816 lon11 = parameter_to_int(cdo_operator_argv(argc_offset + 0));
817 lon12 = parameter_to_int(cdo_operator_argv(argc_offset + 1));
818 lat1 = parameter_to_int(cdo_operator_argv(argc_offset + 2));
819 lat2 = parameter_to_int(cdo_operator_argv(argc_offset + 3));
820
821 if (lat1 > lat2) std::swap(lat1, lat2);
822
823 const long nlon = gridInqXsize(gridID1);
824 const long nlat = gridInqYsize(gridID1);
825
826 checkBounds("First latitude", 1, nlat, lat1);
827 checkBounds("Last latitude", 1, nlat, lat2);
828 checkBounds("First longitude", 1, nlon, lon11);
829 checkBounds("Last longitude", 1, nlon, lon12);
830
831 lon11--;
832 lon12--;
833 lat1--;
834 lat2--;
835
836 if (lon11 > lon12)
837 {
838 lon21 = lon11;
839 lon22 = nlon - 1;
840 lon11 = 0;
841 }
842 else
843 {
844 if (lon12 > nlon - 1)
845 {
846 lon21 = lon11;
847 lon22 = nlon - 1;
848 lon11 = 0;
849 lon12 = 0;
850 }
851 else
852 {
853 lon21 = 0;
854 lon22 = -1;
855 }
856 }
857 }
858
859 static int
genindexgrid(const int gridID1,SelboxInfo & sbox)860 genindexgrid(const int gridID1, SelboxInfo &sbox)
861 {
862 genindexbox(0, gridID1, sbox);
863
864 if (gridInqType(gridID1) == GRID_PROJECTION && gridInqProjType(gridID1) == CDI_PROJ_LCC)
865 return cdo_define_subgrid_grid(gridID1, sbox.lon11, sbox.lon12, sbox.lat1, sbox.lat2);
866 else
867 return gengrid(gridID1, sbox);
868 }
869
870 template <typename T>
871 static void
window_box(const int nwpv,const T * array1,const int gridID1,T * array2,const long lat1,const long lat2,const long lon11,const long lon12,const long lon21,const long lon22)872 window_box(const int nwpv, const T *array1, const int gridID1, T *array2, const long lat1, const long lat2, const long lon11,
873 const long lon12, const long lon21, const long lon22)
874 {
875 const long nlon = gridInqXsize(gridID1);
876
877 if (nwpv == 2)
878 {
879 for (long ilat = lat1; ilat <= lat2; ilat++)
880 {
881 for (long ilon = lon21; ilon <= lon22; ilon++)
882 {
883 *array2++ = array1[ilat * nlon * 2 + ilon * 2];
884 *array2++ = array1[ilat * nlon * 2 + ilon * 2 + 1];
885 }
886 for (long ilon = lon11; ilon <= lon12; ilon++)
887 {
888 *array2++ = array1[ilat * nlon * 2 + ilon * 2];
889 *array2++ = array1[ilat * nlon * 2 + ilon * 2 + 1];
890 }
891 }
892 }
893 else
894 {
895 for (long ilat = lat1; ilat <= lat2; ilat++)
896 {
897 for (long ilon = lon21; ilon <= lon22; ilon++) *array2++ = array1[ilat * nlon + ilon];
898 for (long ilon = lon11; ilon <= lon12; ilon++) *array2++ = array1[ilat * nlon + ilon];
899 }
900 }
901 }
902
903 static void
window_box(const Field & field1,Field & field2,const long lat1,const long lat2,const long lon11,const long lon12,const long lon21,const long lon22)904 window_box(const Field &field1, Field &field2, const long lat1, const long lat2, const long lon11, const long lon12,
905 const long lon21, const long lon22)
906 {
907 if (field1.memType == MemType::Float)
908 window_box(field1.nwpv, field1.vec_f.data(), field1.grid, field2.vec_f.data(), lat1, lat2, lon11, lon12, lon21, lon22);
909 else
910 window_box(field1.nwpv, field1.vec_d.data(), field1.grid, field2.vec_d.data(), lat1, lat2, lon11, lon12, lon21, lon22);
911 }
912
913 template <typename T>
914 static void
window_cell(const int nwpv,const Varray<T> & array1,Varray<T> & array2,const long gridsize2,const std::vector<long> & cellidx)915 window_cell(const int nwpv, const Varray<T> &array1, Varray<T> &array2, const long gridsize2, const std::vector<long> &cellidx)
916 {
917 if (nwpv == 2)
918 {
919 for (long i = 0; i < gridsize2; ++i)
920 {
921 array2[i * 2] = array1[cellidx[i] * 2];
922 array2[i * 2 + 1] = array1[cellidx[i] * 2 + 1];
923 }
924 }
925 else
926 {
927 for (long i = 0; i < gridsize2; ++i) array2[i] = array1[cellidx[i]];
928 }
929 }
930
931 void
window_cell(const Field & field1,Field & field2,const std::vector<long> & cellidx)932 window_cell(const Field &field1, Field &field2, const std::vector<long> &cellidx)
933 {
934 if (field1.memType == MemType::Float)
935 window_cell(field1.nwpv, field1.vec_f, field2.vec_f, field2.gridsize, cellidx);
936 else
937 window_cell(field1.nwpv, field1.vec_d, field2.vec_d, field2.gridsize, cellidx);
938 }
939
940 void *
Selbox(void * process)941 Selbox(void *process)
942 {
943 int varID, levelID;
944 int index;
945
946 cdo_initialize(process);
947
948 // clang-format off
949 const auto SELLONLATBOX = cdo_operator_add("sellonlatbox", 0, 0, "western and eastern longitude and southern and northern latitude");
950 const auto SELINDEXBOX = cdo_operator_add("selindexbox", 0, 0, "index of first and last longitude and index of first and last latitude");
951 // clang-format on
952
953 const auto operatorID = cdo_operator_id();
954
955 operator_input_arg(cdo_operator_enter(operatorID));
956
957 const auto streamID1 = cdo_open_read(0);
958
959 const auto vlistID1 = cdo_stream_inq_vlist(streamID1);
960 const auto vlistID2 = vlistDuplicate(vlistID1);
961
962 const auto taxisID1 = vlistInqTaxis(vlistID1);
963 const auto taxisID2 = taxisDuplicate(taxisID1);
964 vlistDefTaxis(vlistID2, taxisID2);
965
966 const auto nvars = vlistNvars(vlistID1);
967 std::vector<bool> vars(nvars, false);
968
969 const auto ngrids = vlistNgrids(vlistID1);
970 std::vector<SelboxInfo> sbox(ngrids);
971
972 for (index = 0; index < ngrids; index++)
973 {
974 auto &sb = sbox[index];
975 const auto gridID1 = vlistGrid(vlistID1, index);
976 const auto gridtype = gridInqType(gridID1);
977 const auto projtype = gridInqProjType(gridID1);
978
979 const auto lprojection = (gridtype == GRID_PROJECTION && projtype == CDI_PROJ_RLL);
980 // || (gridtype == GRID_PROJECTION && projtype == CDI_PROJ_LCC)
981 // || (gridtype == GRID_PROJECTION && projtype == CDI_PROJ_STERE);
982 if (gridtype == GRID_LONLAT || gridtype == GRID_GAUSSIAN || gridtype == GRID_CURVILINEAR || lprojection
983 || (operatorID == SELINDEXBOX && (gridtype == GRID_GENERIC || gridtype == GRID_PROJECTION) && gridInqXsize(gridID1) > 0
984 && gridInqYsize(gridID1) > 0)
985 || (operatorID == SELLONLATBOX && gridtype == GRID_UNSTRUCTURED))
986 {
987 int gridID2;
988 if (operatorID == SELLONLATBOX)
989 {
990 const auto gridsize = gridInqSize(gridID1);
991 if (gridsize == 1) continue;
992
993 if (gridtype == GRID_UNSTRUCTURED)
994 gridID2 = gencellgrid(gridID1, sb.nvals, sb.cellidx);
995 else
996 gridID2 = genlonlatgrid(gridID1, sb);
997 }
998 else
999 gridID2 = genindexgrid(gridID1, sb);
1000
1001 sb.gridtype = gridtype;
1002 sb.gridID1 = gridID1;
1003 sb.gridID2 = gridID2;
1004
1005 vlistChangeGridIndex(vlistID2, index, gridID2);
1006
1007 for (varID = 0; varID < nvars; varID++)
1008 if (gridID1 == vlistInqVarGrid(vlistID1, varID)) vars[varID] = true;
1009 }
1010 else if (gridtype == GRID_GENERIC && gridInqXsize(gridID1) <= 1 && gridInqYsize(gridID1) <= 1)
1011 {
1012 }
1013 else
1014 {
1015 cdo_print("Unsupported grid type: %s", gridNamePtr(gridtype));
1016 if (gridtype == GRID_GAUSSIAN_REDUCED) cdo_print("Use option -R to convert Gaussian reduced grid to a regular grid!");
1017 cdo_abort("Unsupported grid type!");
1018 }
1019 }
1020
1021 if (Options::cdoVerbose)
1022 {
1023 for (const auto &sb : sbox)
1024 if (sb.gridtype != GRID_UNSTRUCTURED)
1025 {
1026 cdo_print("box1 - idx1,idx2,idy1,idy2: %ld,%ld,%ld,%ld", sb.lon21 + 1, sb.lon22 + 1, sb.lat1 + 1, sb.lat2 + 1);
1027 cdo_print("box2 - idx1,idx2,idy1,idy2: %ld,%ld,%ld,%ld", sb.lon11 + 1, sb.lon12 + 1, sb.lat1 + 1, sb.lat2 + 1);
1028 }
1029 }
1030
1031 for (varID = 0; varID < nvars; varID++)
1032 if (vars[varID]) break;
1033
1034 if (varID >= nvars) cdo_warning("No variables selected!");
1035
1036 const auto streamID2 = cdo_open_write(1);
1037 cdo_def_vlist(streamID2, vlistID2);
1038
1039 VarList varList1, varList2;
1040 varListInit(varList1, vlistID1);
1041 varListInit(varList2, vlistID2);
1042
1043 Field field1, field2;
1044
1045 int tsID = 0;
1046 while (true)
1047 {
1048 const auto nrecs = cdo_stream_inq_timestep(streamID1, tsID);
1049 if (nrecs == 0) break;
1050
1051 cdo_taxis_copy_timestep(taxisID2, taxisID1);
1052 cdo_def_timestep(streamID2, tsID);
1053
1054 for (int recID = 0; recID < nrecs; recID++)
1055 {
1056 cdo_inq_record(streamID1, &varID, &levelID);
1057 field1.init(varList1[varID]);
1058 cdo_read_record(streamID1, field1);
1059
1060 cdo_def_record(streamID2, varID, levelID);
1061
1062 if (vars[varID])
1063 {
1064 field2.init(varList2[varID]);
1065
1066 const auto gridID1 = varList1[varID].gridID;
1067 for (index = 0; index < ngrids; index++)
1068 if (gridID1 == sbox[index].gridID1) break;
1069 if (index == ngrids) cdo_abort("Internal problem, grid not found!");
1070
1071 const auto &sb = sbox[index];
1072
1073 if (operatorID == SELLONLATBOX && sb.gridtype == GRID_UNSTRUCTURED)
1074 window_cell(field1, field2, sb.cellidx);
1075 else
1076 window_box(field1, field2, sb.lat1, sb.lat2, sb.lon11, sb.lon12, sb.lon21, sb.lon22);
1077
1078 if (field1.nmiss) field_num_mv(field2);
1079
1080 cdo_write_record(streamID2, field2);
1081 }
1082 else
1083 {
1084 cdo_write_record(streamID2, field1);
1085 }
1086 }
1087
1088 tsID++;
1089 }
1090
1091 cdo_stream_close(streamID2);
1092 cdo_stream_close(streamID1);
1093
1094 vlistDestroy(vlistID2);
1095
1096 cdo_finish();
1097
1098 return nullptr;
1099 }
1100