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