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 #ifdef _OPENMP
9 #include <omp.h>
10 #endif
11 
12 #include <stdio.h>
13 
14 #include <vector>
15 
16 #include <cdi.h>
17 #include "mpim_grid.h"
18 #include "grid_proj.h"
19 #include "grid_convert.h"
20 #include "grid_rot.h"
21 #include "grid_rot.h"
22 #include "gridreference.h"
23 
24 #include "compare.h"
25 #include "cdo_output.h"
26 
27 bool gridVerbose = false;
28 
29 void
gridEnableVerbose(bool enable)30 gridEnableVerbose(bool enable)
31 {
32   gridVerbose = enable;
33 }
34 
35 int
nfc_to_nlat(int nfc,int ntr)36 nfc_to_nlat(int nfc, int ntr)
37 {
38   return (nfc / (ntr + 1)) / 2;
39 }
40 
41 int
nlat_to_ntr(int nlat)42 nlat_to_ntr(int nlat)
43 {
44   return (nlat * 2 - 1) / 3;
45 }
46 
47 int
nlat_to_ntr_linear(int nlat)48 nlat_to_ntr_linear(int nlat)
49 {
50   return (nlat * 2 - 1) / 2;
51 }
52 
53 int
nlat_to_ntr_cubic(int nlat)54 nlat_to_ntr_cubic(int nlat)
55 {
56   return (nlat * 2 - 1) / 4;
57 }
58 
59 int
ntr_to_nlat(int ntr)60 ntr_to_nlat(int ntr)
61 {
62   auto nlat = (int) std::lround((ntr * 3. + 1.) / 2.);
63   if ((nlat % 2) > 0) nlat++;
64 
65   return nlat;
66 }
67 
68 int
ntr_to_nlat_linear(int ntr)69 ntr_to_nlat_linear(int ntr)
70 {
71   auto nlat = (int) std::lround((ntr * 2. + 1.) / 2.);
72   if ((nlat % 2) > 0) nlat++;
73 
74   return nlat;
75 }
76 
77 int
ntr_to_nlat_cubic(int ntr)78 ntr_to_nlat_cubic(int ntr)
79 {
80   auto nlat = (int) std::lround((ntr * 4. + 1.) / 2.);
81   if ((nlat % 2) > 0) nlat++;
82 
83   return nlat;
84 }
85 
86 int
nlat_to_nlon(int nlat)87 nlat_to_nlon(int nlat)
88 {
89   return 2 * nlat;
90 }
91 
92 int
nlat_to_nlon_cubic(int nlat)93 nlat_to_nlon_cubic(int nlat)
94 {
95   return 2 * nlat + 16;
96 }
97 
98 static void
scale_vec(double scalefactor,size_t nvals,double * values)99 scale_vec(double scalefactor, size_t nvals, double *values)
100 {
101 #ifdef _OPENMP
102 #pragma omp parallel for default(none) shared(nvals, scalefactor, values)
103 #endif
104   for (size_t n = 0; n < nvals; ++n) values[n] *= scalefactor;
105 }
106 
107 static void
grid_copy_key(int gridID1,int gridID2,int varID,int key)108 grid_copy_key(int gridID1, int gridID2, int varID, int key)
109 {
110   char string[CDI_MAX_NAME];
111   int length = CDI_MAX_NAME;
112   cdiInqKeyString(gridID1, varID, key, string, &length);
113   if (string[0]) cdiDefKeyString(gridID2, varID, key, string);
114 }
115 
116 void
grid_copy_keys(int gridID1,int gridID2)117 grid_copy_keys(int gridID1, int gridID2)
118 {
119   grid_copy_key(gridID1, gridID2, CDI_GLOBAL, CDI_KEY_VDIMNAME);
120   grid_copy_key(gridID1, gridID2, CDI_XAXIS, CDI_KEY_DIMNAME);
121   grid_copy_key(gridID1, gridID2, CDI_YAXIS, CDI_KEY_DIMNAME);
122   grid_copy_key(gridID1, gridID2, CDI_XAXIS, CDI_KEY_NAME);
123   grid_copy_key(gridID1, gridID2, CDI_YAXIS, CDI_KEY_NAME);
124   grid_copy_key(gridID1, gridID2, CDI_XAXIS, CDI_KEY_LONGNAME);
125   grid_copy_key(gridID1, gridID2, CDI_YAXIS, CDI_KEY_LONGNAME);
126   grid_copy_key(gridID1, gridID2, CDI_XAXIS, CDI_KEY_UNITS);
127   grid_copy_key(gridID1, gridID2, CDI_YAXIS, CDI_KEY_UNITS);
128 }
129 
130 void
grid_copy_mapping(int gridID1,int gridID2)131 grid_copy_mapping(int gridID1, int gridID2)
132 {
133   grid_copy_key(gridID1, gridID2, CDI_GLOBAL, CDI_KEY_GRIDMAP_VARNAME);
134   grid_copy_key(gridID1, gridID2, CDI_GLOBAL, CDI_KEY_GRIDMAP_NAME);
135 
136   cdiCopyAtts(gridID1, CDI_GLOBAL, gridID2, CDI_GLOBAL);
137 }
138 
139 void
grid_to_radian(const char * units,size_t nvals,double * values,const char * description)140 grid_to_radian(const char *units, size_t nvals, double *values, const char *description)
141 {
142   if (strncmp(units, "deg", 3) == 0)
143     {
144       scale_vec(DEG2RAD, nvals, values);
145     }
146   else if (strncmp(units, "rad", 3) == 0)
147     {
148       /* No conversion necessary */
149     }
150   else
151     {
152       cdo_warning("Unknown units [%s] supplied for %s; proceeding assuming radians!", units, description);
153     }
154 }
155 
156 static void
grid_to_degree(const char * units,size_t nvals,double * values,const char * description)157 grid_to_degree(const char *units, size_t nvals, double *values, const char *description)
158 {
159   if (strncmp(units, "rad", 3) == 0)
160     {
161       for (size_t n = 0; n < nvals; ++n) values[n] *= RAD2DEG;
162     }
163   else if (strncmp(units, "deg", 3) == 0)
164     {
165       /* No conversion necessary */
166     }
167   else
168     {
169       cdo_warning("Unknown units [%s] supplied for %s; proceeding assuming degress!", units, description);
170     }
171 }
172 
173 void
cdo_grid_to_radian(int gridID,int varID,size_t nvals,double * values,const char * description)174 cdo_grid_to_radian(int gridID, int varID, size_t nvals, double *values, const char *description)
175 {
176   char units[CDI_MAX_NAME];
177   int length = CDI_MAX_NAME;
178   cdiInqKeyString(gridID, varID, CDI_KEY_UNITS, units, &length);
179   grid_to_radian(units, nvals, values, description);
180 }
181 
182 void
cdo_grid_to_degree(int gridID,int varID,size_t nvals,double * values,const char * description)183 cdo_grid_to_degree(int gridID, int varID, size_t nvals, double *values, const char *description)
184 {
185   char units[CDI_MAX_NAME];
186   int length = CDI_MAX_NAME;
187   cdiInqKeyString(gridID, varID, CDI_KEY_UNITS, units, &length);
188   grid_to_degree(units, nvals, values, description);
189 }
190 
191 int
gridToZonal(const int gridID1)192 gridToZonal(const int gridID1)
193 {
194   int gridID2 = CDI_UNDEFID;
195 
196   auto gridtype = gridInqType(gridID1);
197   if (gridtype == GRID_LONLAT || gridtype == GRID_GAUSSIAN || gridtype == GRID_GAUSSIAN_REDUCED || gridtype == GRID_GENERIC)
198     {
199       if (gridtype == GRID_GAUSSIAN_REDUCED) gridtype = GRID_GAUSSIAN;
200 
201       const auto gridsize = gridInqYsize(gridID1);
202       gridID2 = gridCreate(gridtype, gridsize);
203 
204       gridDefXsize(gridID2, 1);
205       gridDefYsize(gridID2, gridsize);
206 
207       if (gridtype == GRID_GAUSSIAN) gridDefNP(gridID2, gridInqNP(gridID1));
208 
209       const double xval = 0.0;
210       gridDefXvals(gridID2, &xval);
211 
212       if (gridInqYvals(gridID1, nullptr))
213         {
214           std::vector<double> yvals(gridsize);
215           gridInqYvals(gridID1, yvals.data());
216           gridDefYvals(gridID2, yvals.data());
217         }
218     }
219   else
220     {
221       cdo_abort("Gridtype %s unsupported!", gridNamePtr(gridtype));
222     }
223 
224   return gridID2;
225 }
226 
227 int
gridToMeridional(const int gridID1)228 gridToMeridional(const int gridID1)
229 {
230   const auto gridtype = gridInqType(gridID1);
231   const auto gridsize = gridInqXsize(gridID1);
232   const auto gridID2 = gridCreate(gridtype, gridsize);
233 
234   if (gridtype == GRID_LONLAT || gridtype == GRID_GAUSSIAN || gridtype == GRID_GENERIC)
235     {
236       gridDefXsize(gridID2, gridsize);
237       gridDefYsize(gridID2, 1);
238 
239       if (gridInqXvals(gridID1, nullptr))
240         {
241           std::vector<double> xvals(gridsize);
242           gridInqXvals(gridID1, xvals.data());
243           gridDefXvals(gridID2, xvals.data());
244         }
245 
246       const double yval = 0.0;
247       gridDefYvals(gridID2, &yval);
248     }
249   else
250     {
251       cdo_abort("Gridtype %s unsupported!", gridNamePtr(gridtype));
252     }
253 
254   return gridID2;
255 }
256 
257 void
grid_gen_corners(size_t n,const double * vals,double * corners)258 grid_gen_corners(size_t n, const double *vals, double *corners)
259 {
260   if (n == 1)
261     {
262       corners[0] = vals[0];
263       corners[1] = vals[0];
264     }
265   else
266     {
267       for (size_t i = 0; i < n - 1; ++i) corners[i + 1] = 0.5 * (vals[i] + vals[i + 1]);
268 
269       corners[0] = 2 * vals[0] - corners[1];
270       corners[n] = 2 * vals[n - 1] - corners[n - 1];
271     }
272 }
273 
274 void
grid_gen_bounds(size_t n,const std::vector<double> & vals,std::vector<double> & bounds)275 grid_gen_bounds(size_t n, const std::vector<double> &vals, std::vector<double> &bounds)
276 {
277   const auto lrev = (vals[0] > vals[n - 1]);
278   if (lrev)
279     {
280       for (size_t i = 0; i < n - 1; ++i)
281         {
282           bounds[2 * i] = 0.5 * (vals[i] + vals[i + 1]);
283           bounds[2 * (i + 1) + 1] = 0.5 * (vals[i] + vals[i + 1]);
284         }
285 
286       bounds[1] = 2 * vals[0] - bounds[0];
287       bounds[2 * n - 2] = 2 * vals[n - 1] - bounds[2 * n - 1];
288     }
289   else
290     {
291       for (size_t i = 0; i < n - 1; ++i)
292         {
293           bounds[2 * i + 1] = 0.5 * (vals[i] + vals[i + 1]);
294           bounds[2 * (i + 1)] = 0.5 * (vals[i] + vals[i + 1]);
295         }
296 
297       bounds[0] = 2 * vals[0] - bounds[1];
298       bounds[2 * n - 1] = 2 * vals[n - 1] - bounds[2 * (n - 1)];
299     }
300 }
301 
302 void
grid_check_lat_borders(int n,double * ybounds)303 grid_check_lat_borders(int n, double *ybounds)
304 {
305   constexpr double YMAX = 90.0;
306   constexpr double YLIM = 88.0;
307   const auto lrev = (ybounds[0] > ybounds[n - 1]);
308   if (lrev)
309     {
310       if (ybounds[0] > ybounds[1])
311         {
312           if (ybounds[0] > YLIM) ybounds[0] = YMAX;
313           if (ybounds[n - 1] < -YLIM) ybounds[n - 1] = -YMAX;
314         }
315       else
316         {
317           if (ybounds[1] > YLIM) ybounds[1] = YMAX;
318           if (ybounds[n - 2] < -YLIM) ybounds[n - 2] = -YMAX;
319         }
320     }
321   else
322     {
323       if (ybounds[0] < ybounds[1])
324         {
325           if (ybounds[0] < -YLIM) ybounds[0] = -YMAX;
326           if (ybounds[n - 1] > YLIM) ybounds[n - 1] = YMAX;
327         }
328       else
329         {
330           if (ybounds[1] < -YLIM) ybounds[1] = -YMAX;
331           if (ybounds[n - 2] > YLIM) ybounds[n - 2] = YMAX;
332         }
333     }
334 }
335 
336 /*****************************************************************************/
337 
338 static void
gridGenCenterRLL(int gridID,size_t nx,size_t ny,const std::vector<double> & xvals,const std::vector<double> & yvals,std::vector<double> & xvals2D,std::vector<double> & yvals2D)339 gridGenCenterRLL(int gridID, size_t nx, size_t ny, const std::vector<double> &xvals, const std::vector<double> &yvals,
340                  std::vector<double> &xvals2D, std::vector<double> &yvals2D)
341 {
342   double xpole = 0.0, ypole = 0.0, angle = 0.0;
343   gridInqParamRLL(gridID, &xpole, &ypole, &angle);
344 
345   for (size_t j = 0; j < ny; j++)
346     for (size_t i = 0; i < nx; i++)
347       {
348         xvals2D[j * nx + i] = lamrot_to_lam(yvals[j], xvals[i], ypole, xpole, angle);
349         yvals2D[j * nx + i] = phirot_to_phi(yvals[j], xvals[i], ypole, angle);
350       }
351 }
352 
353 static void
gridGenBoundsRLL(int gridID,size_t nx,size_t ny,const std::vector<double> & xbounds,const std::vector<double> & ybounds,std::vector<double> & xbounds2D,std::vector<double> & ybounds2D)354 gridGenBoundsRLL(int gridID, size_t nx, size_t ny, const std::vector<double> &xbounds, const std::vector<double> &ybounds,
355                  std::vector<double> &xbounds2D, std::vector<double> &ybounds2D)
356 {
357   double xpole = 0.0, ypole = 0.0, angle = 0.0;
358   gridInqParamRLL(gridID, &xpole, &ypole, &angle);
359 
360   double minlon, maxlon;
361   double minlat, maxlat;
362 
363   for (size_t j = 0; j < ny; j++)
364     {
365       if (ybounds[0] > ybounds[1])
366         {
367           maxlat = ybounds[2 * j];
368           minlat = ybounds[2 * j + 1];
369         }
370       else
371         {
372           maxlat = ybounds[2 * j + 1];
373           minlat = ybounds[2 * j];
374         }
375 
376       for (size_t i = 0; i < nx; i++)
377         {
378           minlon = xbounds[2 * i];
379           maxlon = xbounds[2 * i + 1];
380 
381           size_t index = j * 4 * nx + 4 * i;
382           xbounds2D[index + 0] = lamrot_to_lam(minlat, minlon, ypole, xpole, angle);
383           xbounds2D[index + 1] = lamrot_to_lam(minlat, maxlon, ypole, xpole, angle);
384           xbounds2D[index + 2] = lamrot_to_lam(maxlat, maxlon, ypole, xpole, angle);
385           xbounds2D[index + 3] = lamrot_to_lam(maxlat, minlon, ypole, xpole, angle);
386 
387           ybounds2D[index + 0] = phirot_to_phi(minlat, minlon, ypole, angle);
388           ybounds2D[index + 1] = phirot_to_phi(minlat, maxlon, ypole, angle);
389           ybounds2D[index + 2] = phirot_to_phi(maxlat, maxlon, ypole, angle);
390           ybounds2D[index + 3] = phirot_to_phi(maxlat, minlon, ypole, angle);
391         }
392     }
393 }
394 
395 void
grid_gen_xbounds2D(size_t nx,size_t ny,const std::vector<double> & xbounds,std::vector<double> & xbounds2D)396 grid_gen_xbounds2D(size_t nx, size_t ny, const std::vector<double> &xbounds, std::vector<double> &xbounds2D)
397 {
398 #ifdef _OPENMP
399 #pragma omp parallel for default(none) shared(nx, ny, xbounds, xbounds2D)
400 #endif
401   for (size_t i = 0; i < nx; ++i)
402     {
403       const auto minlon = (xbounds[0] > xbounds[1]) ? xbounds[2 * i + 1] : xbounds[2 * i];
404       const auto maxlon = (xbounds[0] > xbounds[1]) ? xbounds[2 * i] : xbounds[2 * i + 1];
405 
406       for (size_t j = 0; j < ny; ++j)
407         {
408           const auto index = 4 * (j * nx + i);
409           xbounds2D[index] = minlon;
410           xbounds2D[index + 1] = maxlon;
411           xbounds2D[index + 2] = maxlon;
412           xbounds2D[index + 3] = minlon;
413         }
414     }
415 }
416 
417 void
grid_gen_ybounds2D(size_t nx,size_t ny,const std::vector<double> & ybounds,std::vector<double> & ybounds2D)418 grid_gen_ybounds2D(size_t nx, size_t ny, const std::vector<double> &ybounds, std::vector<double> &ybounds2D)
419 {
420 #ifdef _OPENMP
421 #pragma omp parallel for default(none) shared(nx, ny, ybounds, ybounds2D)
422 #endif
423   for (size_t j = 0; j < ny; ++j)
424     {
425       const auto minlat = (ybounds[0] > ybounds[1]) ? ybounds[2 * j + 1] : ybounds[2 * j];
426       const auto maxlat = (ybounds[0] > ybounds[1]) ? ybounds[2 * j] : ybounds[2 * j + 1];
427 
428       for (size_t i = 0; i < nx; ++i)
429         {
430           const auto index = 4 * (j * nx + i);
431           ybounds2D[index] = minlat;
432           ybounds2D[index + 1] = minlat;
433           ybounds2D[index + 2] = maxlat;
434           ybounds2D[index + 3] = maxlat;
435         }
436     }
437 }
438 
439 /*
440  * grib_get_reduced_row: code from GRIB_API 1.10.4
441  *
442  * Description:
443  *   computes the number of points within the range lon_first->lon_last and the zero based indexes ilon_first,ilon_last
444  *   of the first and last point given the number of points along a parallel (pl)
445  *
446  */
447 static void
grib_get_reduced_row(long pl,double lon_first,double lon_last,long * npoints,long * ilon_first,long * ilon_last)448 grib_get_reduced_row(long pl, double lon_first, double lon_last, long *npoints, long *ilon_first, long *ilon_last)
449 {
450   auto range = lon_last - lon_first;
451   if (range < 0.0)
452     {
453       range += 360.0;
454       lon_first -= 360.0;
455     }
456 
457   // computing integer number of points and coordinates without using floating point resolution
458   *npoints = (range * pl) / 360.0 + 1;
459   *ilon_first = (lon_first * pl) / 360.0;
460   *ilon_last = (lon_last * pl) / 360.0;
461 
462   auto irange = *ilon_last - *ilon_first + 1;
463 
464   if (irange != *npoints)
465     {
466       if (irange > *npoints)
467         {
468           // checking if the first point is out of range
469           const auto dlon_first = ((*ilon_first) * 360.0) / pl;
470           if (dlon_first < lon_first)
471             {
472               (*ilon_first)++;
473               irange--;
474             }
475 
476           // checking if the last point is out of range
477           const auto dlon_last = ((*ilon_last) * 360.0) / pl;
478           if (dlon_last > lon_last)
479             {
480               (*ilon_last)--;
481               irange--;
482             }
483         }
484       else
485         {
486           int ok = 0;
487           // checking if the point before the first is in the range
488           const auto dlon_first = ((*ilon_first - 1) * 360.0) / pl;
489           if (dlon_first > lon_first)
490             {
491               (*ilon_first)--;
492               irange++;
493               ok = 1;
494             }
495 
496           // checking if the point after the last is in the range
497           const auto dlon_last = ((*ilon_last + 1) * 360.0) / pl;
498           if (dlon_last < lon_last)
499             {
500               (*ilon_last)++;
501               irange++;
502               ok = 1;
503             }
504 
505           // if neither of the two are triggered then npoints is too large
506           if (!ok) (*npoints)--;
507         }
508 
509       //   assert(*npoints==irange);
510     }
511   else
512     {
513       // checking if the first point is out of range
514       const auto dlon_first = ((*ilon_first) * 360.0) / pl;
515       if (dlon_first < lon_first)
516         {
517           (*ilon_first)++;
518           (*ilon_last)++;
519         }
520     }
521 
522   if (*ilon_first < 0) *ilon_first += pl;
523 }
524 
525 static int
qu2reg_subarea(size_t gridsize,int np,double xfirst,double xlast,double * array,int * reducedPoints,int ny,double missval,int * iret,int lmiss,int lperio,int lveggy)526 qu2reg_subarea(size_t gridsize, int np, double xfirst, double xlast, double *array, int *reducedPoints, int ny, double missval,
527                int *iret, int lmiss, int lperio, int lveggy)
528 {
529   // sub area (longitudes)
530   long ilon_firstx;
531   long ilon_first, ilon_last;
532   int i, j;
533   long row_count;
534   int rlon;
535   int np4 = np * 4;
536   size_t size = 0;
537   int wlen;
538   int ii;
539 
540   if (np <= 0) cdo_abort("Number of values between pole and equator missing!");
541 
542   grib_get_reduced_row(np4, xfirst, xlast, &row_count, &ilon_firstx, &ilon_last);
543   int nx = row_count;
544   // printf("nx %d  %ld %ld lon1 %g lon2 %g\n", nx, ilon_firstx, ilon_last,
545   // (ilon_firstx*360.)/np4, (ilon_last*360.)/np4);
546 
547   // int nwork = 0;
548   // for (j = 0; j < ny; ++j) nwork += reducedPoints[j];
549 
550   double **pwork = (double **) malloc(ny * sizeof(double *));
551   double *work = (double *) malloc(ny * np4 * sizeof(double));
552   wlen = 0;
553   pwork[0] = work;
554   for (j = 1; j < ny; ++j)
555     {
556       wlen += reducedPoints[j - 1];
557       pwork[j] = work + wlen;
558     }
559   // printf(" ny, np4, nwork %d %d %d wlen %d\n", ny, np4, nwork, wlen);
560 
561   for (j = 0; j < ny; ++j)
562     {
563       rlon = reducedPoints[j];
564       for (i = 0; i < rlon; ++i) pwork[j][i] = missval;
565     }
566 
567   double *parray = array;
568   for (j = 0; j < ny; ++j)
569     {
570       rlon = reducedPoints[j];
571       row_count = 0;
572       grib_get_reduced_row(rlon, xfirst, xlast, &row_count, &ilon_first, &ilon_last);
573       // printf("j %d xfirst %g xlast %g reducedPoints %d %ld %ld %ld %g %g\n", j,
574       // xfirst, xlast, rlon, row_count, ilon_first, ilon_last,
575       // (ilon_first*360.)/rlon, (ilon_last*360.)/rlon);
576 
577       for (i = ilon_first; i < (ilon_first + row_count); ++i)
578         {
579           ii = i;
580           if (ii >= rlon) ii -= rlon;
581           pwork[j][ii] = *parray;
582           parray++;
583         }
584       size += row_count;
585     }
586 
587   if (gridsize != size) cdo_abort("gridsize1 inconsistent! (gridsize=%zu found=%zu)", gridsize, size);
588 
589   qu2reg3_double(work, reducedPoints, ny, np4, missval, iret, lmiss, lperio, lveggy);
590 
591   wlen = 0;
592   pwork[0] = work;
593   for (j = 1; j < ny; ++j)
594     {
595       wlen += np4;
596       pwork[j] = work + wlen;
597     }
598 
599   // printf("nx, ilon_firstx %d %ld\n", nx, ilon_firstx);
600   parray = array;
601   for (j = 0; j < ny; ++j)
602     {
603       for (i = ilon_firstx; i < (ilon_firstx + nx); ++i)
604         {
605           ii = i;
606           if (ii >= np4) ii -= np4;
607           *parray = pwork[j][ii];
608           parray++;
609         }
610     }
611 
612   free(work);
613   free(pwork);
614 
615   return nx;
616 }
617 
618 static void
get_xfirst_and_xlast(int gridID,double & xfirst,double & xlast)619 get_xfirst_and_xlast(int gridID, double &xfirst, double &xlast)
620 {
621   double xfirstandlast[2] = { 0.0, 0.0 };
622   gridInqXvals(gridID, xfirstandlast);
623   if (IS_NOT_EQUAL(xfirstandlast[0], xfirstandlast[1]))
624     {
625       xfirst = xfirstandlast[0];
626       xlast = xfirstandlast[1];
627       if (xfirst > xlast && xfirst > 180.0) xfirst -= 360.0;
628     }
629 }
630 
631 static bool
reduced_grid_is_global(int np,int nxmax,double xfirst,double xlast)632 reduced_grid_is_global(int np, int nxmax, double xfirst, double xlast)
633 {
634   const auto dx_global = (np > 0) ? (90.0 / np) : 999.0;
635   auto dx_data = 360.0 - (xlast - xfirst);
636   if ((dx_data > dx_global) && (dx_data * nxmax > 360.0)) dx_data = 360.0 / nxmax;
637   return !(dx_data > dx_global);
638 }
639 
640 void
field2regular(int gridID1,int gridID2,double missval,double * array,size_t nmiss,int lnearest)641 field2regular(int gridID1, int gridID2, double missval, double *array, size_t nmiss, int lnearest)
642 {
643   const auto gridtype = gridInqType(gridID1);
644   if (gridtype != GRID_GAUSSIAN_REDUCED) cdo_abort("Not a reduced Gaussian grid!");
645 
646   auto nx = gridInqXsize(gridID1);
647   const auto ny = gridInqYsize(gridID1);
648   const auto np = gridInqNP(gridID1);
649 
650   std::vector<int> reducedPoints(ny);
651   gridInqReducedPoints(gridID1, reducedPoints.data());
652 
653   double xfirst = 0.0, xlast = 359.9999;
654   if (nx == 2) get_xfirst_and_xlast(gridID1, xfirst, xlast);
655 
656   int nxmax = 0;
657   for (size_t i = 0; i < ny; ++i) nxmax = std::max(nxmax, reducedPoints[i]);
658 
659   const int lmiss = (nmiss > 0);
660   const int lperio = 1;
661 
662   int iret;
663   if (reduced_grid_is_global(np, nxmax, xfirst, xlast))
664     {
665       nx = gridInqXsize(gridID2);
666       qu2reg3_double(array, reducedPoints.data(), ny, nx, missval, &iret, lmiss, lperio, lnearest);
667     }
668   else
669     {
670       nx = qu2reg_subarea(gridInqSize(gridID1), np, xfirst, xlast, array, reducedPoints.data(), ny, missval, &iret, lmiss, lperio,
671                           lnearest);
672     }
673 
674   if (gridInqSize(gridID2) != nx * ny) cdo_abort("Gridsize differ!");
675 }
676 
677 int
gridToRegular(int gridID1)678 gridToRegular(int gridID1)
679 {
680   const auto gridtype = gridInqType(gridID1);
681   if (gridtype != GRID_GAUSSIAN_REDUCED) cdo_abort("Not a reduced Gaussian grid!");
682 
683   auto nx = gridInqXsize(gridID1);
684   const auto ny = gridInqYsize(gridID1);
685   const auto np = gridInqNP(gridID1);
686 
687   std::vector<double> xvals, yvals(ny);
688   gridInqYvals(gridID1, yvals.data());
689 
690   std::vector<int> reducedPoints(ny);
691   gridInqReducedPoints(gridID1, reducedPoints.data());
692 
693   double xfirst = 0.0, xlast = 359.9999;
694   if (nx == 2) get_xfirst_and_xlast(gridID1, xfirst, xlast);
695 
696   int nxmax = 0;
697   for (size_t i = 0; i < ny; ++i) nxmax = std::max(nxmax, reducedPoints[i]);
698 
699   if (reduced_grid_is_global(np, nxmax, xfirst, xlast))
700     {
701       nx = reducedPoints[ny / 2];
702       if (nx < (2 * ny)) cdo_abort("Number of longitudes %zu is less than 2*ny=%zu!", nx, ny * 2);
703       xvals.resize(nx);
704       for (size_t i = 0; i < nx; ++i) xvals[i] = xfirst + i * 360.0 / nx;
705     }
706   else
707     {
708       if (np <= 0) cdo_abort("Number of values between pole and equator missing!");
709 
710       // sub area (longitudes)
711       const auto np4 = np * 4;
712       long ilon_first, ilon_last, row_count;
713       grib_get_reduced_row(np4, xfirst, xlast, &row_count, &ilon_first, &ilon_last);
714 
715       nx = row_count;
716       xvals.resize(nx);
717       for (size_t i = 0; i < nx; ++i)
718         {
719           xvals[i] = ((ilon_first + i) * 360.0) / np4;
720           if (xfirst > xlast) xvals[i] -= 360.0;
721         }
722     }
723 
724   const auto gridsize = nx * ny;
725   const auto gridID2 = gridCreate(GRID_GAUSSIAN, gridsize);
726 
727   gridDefXsize(gridID2, nx);
728   gridDefYsize(gridID2, ny);
729 
730   gridDefXvals(gridID2, xvals.data());
731   gridDefYvals(gridID2, yvals.data());
732   gridDefNP(gridID2, np);
733 
734   return gridID2;
735 }
736 
737 static void
gridCopyMask(int gridID1,int gridID2,long gridsize)738 gridCopyMask(int gridID1, int gridID2, long gridsize)
739 {
740   if (gridInqMask(gridID1, nullptr))
741     {
742       std::vector<int> mask(gridsize);
743       gridInqMask(gridID1, mask.data());
744       gridDefMask(gridID2, mask.data());
745     }
746 }
747 
748 static bool
check_range(long n,double * vals,double valid_min,double valid_max)749 check_range(long n, double *vals, double valid_min, double valid_max)
750 {
751   bool status = false;
752 
753   for (long i = 0; i < n; ++i)
754     {
755       if (vals[i] < valid_min || vals[i] > valid_max)
756         {
757           status = true;
758           break;
759         }
760     }
761 
762   return status;
763 }
764 
765 bool
grid_has_proj_params(int gridID)766 grid_has_proj_params(int gridID)
767 {
768   bool has_proj_params = false;
769 
770   const auto gridtype = gridInqType(gridID);
771   if (gridtype == GRID_PROJECTION)
772     {
773       int atttype, attlen;
774       char attname[CDI_MAX_NAME + 1];
775 
776       int natts;
777       cdiInqNatts(gridID, CDI_GLOBAL, &natts);
778 
779       for (int iatt = 0; iatt < natts; ++iatt)
780         {
781           cdiInqAtt(gridID, CDI_GLOBAL, iatt, attname, &atttype, &attlen);
782 
783           if (atttype == CDI_DATATYPE_TXT)
784             {
785               if (cdo_cmpstr(attname, "proj_params") || cdo_cmpstr(attname, "proj4_params"))
786                 {
787                   has_proj_params = true;
788                   break;
789                 }
790             }
791         }
792     }
793 
794   return has_proj_params;
795 }
796 
797 static std::vector<char>
grid_get_proj_params(int gridID)798 grid_get_proj_params(int gridID)
799 {
800   std::vector<char> proj_params;
801 
802   const auto gridtype = gridInqType(gridID);
803   if (gridtype == GRID_PROJECTION)
804     {
805       int atttype, attlen, atttxtlen = 0;
806       std::vector<char> atttxt;
807       char attname[CDI_MAX_NAME + 1];
808 
809       int natts;
810       cdiInqNatts(gridID, CDI_GLOBAL, &natts);
811 
812       for (int iatt = 0; iatt < natts; ++iatt)
813         {
814           cdiInqAtt(gridID, CDI_GLOBAL, iatt, attname, &atttype, &attlen);
815 
816           if (atttype == CDI_DATATYPE_TXT)
817             {
818               if (attlen > atttxtlen)
819                 {
820                   atttxt.resize(attlen + 1);
821                   atttxtlen = attlen;
822                 }
823               cdiInqAttTxt(gridID, CDI_GLOBAL, attname, attlen, atttxt.data());
824               atttxt[attlen] = 0;
825               if (cdo_cmpstr(attname, "proj_params") || cdo_cmpstr(attname, "proj4_params"))
826                 {
827                   proj_params = atttxt;
828                   break;
829                 }
830             }
831         }
832     }
833 
834   return proj_params;
835 }
836 
837 static void
check_units(const char * name,const char * units)838 check_units(const char *name, const char *units)
839 {
840   const auto len = strlen(units);
841   const bool lvalid_units
842       = (len == 1 && *units == 'm') || (len == 2 && memcmp(units, "km", 2) == 0) || (strncmp(units, "meter", 5) == 0);
843 
844   if (!lvalid_units)
845     cdo_warning("Possibly wrong result! %s %s-coordinate units: %s%s%s (expected \"m\" or \"km\"; default \"m\")",
846                 len ? "Invalid" : "Missing", name, len ? "\"" : "", units, len ? "\"" : "");
847 }
848 
849 static void
center_1D_to_2D(size_t nx,size_t ny,const std::vector<double> & xvals,const std::vector<double> & yvals,std::vector<double> & xvals2D,std::vector<double> & yvals2D,double xscale,double yscale)850 center_1D_to_2D(size_t nx, size_t ny, const std::vector<double> &xvals, const std::vector<double> &yvals,
851                 std::vector<double> &xvals2D, std::vector<double> &yvals2D, double xscale, double yscale)
852 {
853   for (size_t j = 0; j < ny; j++)
854     for (size_t i = 0; i < nx; i++)
855       {
856         xvals2D[j * nx + i] = xscale * xvals[i];
857         yvals2D[j * nx + i] = yscale * yvals[j];
858       }
859 }
860 
861 static void
bounds_1D_to_2D(size_t nx,size_t ny,const std::vector<double> & xbounds,const std::vector<double> & ybounds,std::vector<double> & xbounds2D,std::vector<double> & ybounds2D,double xscale,double yscale)862 bounds_1D_to_2D(size_t nx, size_t ny, const std::vector<double> &xbounds, const std::vector<double> &ybounds,
863                 std::vector<double> &xbounds2D, std::vector<double> &ybounds2D, double xscale, double yscale)
864 {
865   for (size_t j = 0; j < ny; j++)
866     for (size_t i = 0; i < nx; i++)
867       {
868         const auto index = 4 * (j * nx + i);
869         xbounds2D[index + 0] = xscale * xbounds[2 * i];
870         ybounds2D[index + 0] = yscale * ybounds[2 * j];
871         xbounds2D[index + 1] = xscale * xbounds[2 * i];
872         ybounds2D[index + 1] = yscale * ybounds[2 * j + 1];
873         xbounds2D[index + 2] = xscale * xbounds[2 * i + 1];
874         ybounds2D[index + 2] = yscale * ybounds[2 * j + 1];
875         xbounds2D[index + 3] = xscale * xbounds[2 * i + 1];
876         ybounds2D[index + 3] = yscale * ybounds[2 * j];
877       }
878 }
879 
880 enum struct Projection
881 {
882   none,
883   proj_params,
884   proj_rll,
885   proj_laea,
886   proj_lcc,
887   proj_sinu,
888   proj_stere
889 };
890 
891 Projection
get_projection(int gridID1)892 get_projection(int gridID1)
893 {
894   Projection projection(Projection::none);
895 
896   const auto projtype = gridInqProjType(gridID1);
897   // clang-format off
898   if      (projtype == CDI_PROJ_RLL)   projection = Projection::proj_rll;
899   else if (projtype == CDI_PROJ_LAEA)  projection = Projection::proj_laea;
900   else if (projtype == CDI_PROJ_LCC)   projection = Projection::proj_lcc;
901   else if (projtype == CDI_PROJ_SINU)  projection = Projection::proj_sinu;
902   else if (projtype == CDI_PROJ_STERE) projection = Projection::proj_stere;
903   else
904     {
905       char gmapname[CDI_MAX_NAME];
906       int length = CDI_MAX_NAME;
907       cdiInqKeyString(gridID1, CDI_GLOBAL, CDI_KEY_GRIDMAP_NAME, gmapname, &length);
908       cdo_abort("Projection type >%s< unsupported!", gmapname);
909     }
910   // clang-format on
911 
912   return projection;
913 }
914 
915 static void
apply_projection(Projection projection,std::vector<char> & proj_params,int gridID,size_t n,double * x,double * y)916 apply_projection(Projection projection, std::vector<char> &proj_params, int gridID, size_t n, double *x, double *y)
917 {
918   // clang-format off
919   if      (projection == Projection::proj_sinu)   cdo_sinu_to_lonlat(n, x, y);
920   else if (projection == Projection::proj_laea)   cdo_laea_to_lonlat(gridID, n, x, y);
921   else if (projection == Projection::proj_lcc)    cdo_lcc_to_lonlat(gridID, n, x, y);
922   else if (projection == Projection::proj_stere)  cdo_stere_to_lonlat(gridID, n, x, y);
923   else if (projection == Projection::proj_params) cdo_proj_to_lonlat(proj_params.data(), n, x, y);
924   // clang-format on
925 }
926 
927 int
gridToCurvilinear(int gridID1,int lbounds)928 gridToCurvilinear(int gridID1, int lbounds)
929 {
930   const auto gridtype = gridInqType(gridID1);
931   if (!(gridtype == GRID_LONLAT || gridtype == GRID_GAUSSIAN || gridtype == GRID_PROJECTION))
932     cdo_abort("%s: Grid type >%s< unsupported!", __func__, gridNamePtr(gridtype));
933 
934   auto nx = gridInqXsize(gridID1);
935   auto ny = gridInqYsize(gridID1);
936 
937   const bool lxyvals = gridHasCoordinates(gridID1);
938   if (!lxyvals) cdo_abort("Grid coordinates missing!");
939 
940   const auto gridsize = gridInqSize(gridID1);
941   const auto gridID2 = gridCreate(GRID_CURVILINEAR, gridsize);
942   cdiDefKeyInt(gridID2, CDI_GLOBAL, CDI_KEY_DATATYPE, CDI_DATATYPE_FLT32);
943 
944   Projection projection(Projection::none);
945   std::vector<char> proj_params;
946   if (gridtype == GRID_PROJECTION && gridsize == nx * ny)
947     {
948       proj_params = grid_get_proj_params(gridID1);
949       projection = !proj_params.empty() ? Projection::proj_params : get_projection(gridID1);
950       // if (projection != Projection::none) gridtype = GRID_LONLAT;
951     }
952 
953   const bool lprojection = projection == Projection::proj_laea || projection == Projection::proj_lcc
954                            || projection == Projection::proj_sinu || projection == Projection::proj_stere;
955 
956   const auto nvertex = (size_t) gridInqNvertex(gridID1);
957 
958   char xunits[CDI_MAX_NAME], yunits[CDI_MAX_NAME];
959   int length = CDI_MAX_NAME;
960   cdiInqKeyString(gridID1, CDI_XAXIS, CDI_KEY_UNITS, xunits, &length);
961   length = CDI_MAX_NAME;
962   cdiInqKeyString(gridID1, CDI_YAXIS, CDI_KEY_UNITS, yunits, &length);
963 
964   if (lprojection) check_units("x", xunits);
965   if (lprojection) check_units("y", yunits);
966 
967   if (lprojection || projection == Projection::proj_params)
968     {
969       char xname[CDI_MAX_NAME], yname[CDI_MAX_NAME];
970       length = CDI_MAX_NAME;
971       cdiInqKeyString(gridID1, CDI_XAXIS, CDI_KEY_NAME, xname, &length);
972       length = CDI_MAX_NAME;
973       cdiInqKeyString(gridID1, CDI_YAXIS, CDI_KEY_NAME, yname, &length);
974 
975       if (xname[0] && yname[0])
976         {
977           cdiDefKeyString(gridID2, CDI_XAXIS, CDI_KEY_DIMNAME, xname);
978           cdiDefKeyString(gridID2, CDI_YAXIS, CDI_KEY_DIMNAME, yname);
979         }
980     }
981 
982   const double xscale = (xunits[0] == 'k' && xunits[1] == 'm') ? 1000.0 : 1.0;
983   const double yscale = (yunits[0] == 'k' && yunits[1] == 'm') ? 1000.0 : 1.0;
984 
985   gridDefXsize(gridID2, nx);
986   gridDefYsize(gridID2, ny);
987 
988   std::vector<double> xvals2D(gridsize), yvals2D(gridsize);
989 
990   if (nx == 0) nx = 1;
991   if (ny == 0) ny = 1;
992 
993   std::vector<double> xvals(nx, 0), yvals(ny, 0);
994   if (gridInqXvals(gridID1, nullptr)) gridInqXvals(gridID1, xvals.data());
995   if (gridInqYvals(gridID1, nullptr)) gridInqYvals(gridID1, yvals.data());
996 
997   if (projection == Projection::proj_rll)
998     {
999       gridDefProj(gridID2, gridID1);
1000       gridGenCenterRLL(gridID1, nx, ny, xvals, yvals, xvals2D, yvals2D);
1001     }
1002   else
1003     {
1004       center_1D_to_2D(nx, ny, xvals, yvals, xvals2D, yvals2D, xscale, yscale);
1005       if (projection != Projection::none)
1006         {
1007           gridDefProj(gridID2, gridID1);
1008           apply_projection(projection, proj_params, gridID1, gridsize, xvals2D.data(), yvals2D.data());
1009         }
1010     }
1011 
1012   gridDefXvals(gridID2, xvals2D.data());
1013   gridDefYvals(gridID2, yvals2D.data());
1014 
1015   if (lbounds)
1016     {
1017       std::vector<double> xbounds, ybounds;
1018 
1019       if (nvertex == 2 && gridInqXbounds(gridID1, nullptr))
1020         {
1021           xbounds.resize(2 * nx);
1022           gridInqXbounds(gridID1, xbounds.data());
1023           if (check_range(2 * nx, xbounds.data(), -720, 720))
1024             {
1025               cdo_warning("longitude bounds out of range, skipped!");
1026               xbounds.clear();
1027             }
1028         }
1029       else if (nx > 1)
1030         {
1031           xbounds.resize(2 * nx);
1032           grid_gen_bounds(nx, xvals, xbounds);
1033         }
1034 
1035       if (nvertex == 2 && gridInqYbounds(gridID1, nullptr))
1036         {
1037           ybounds.resize(2 * ny);
1038           gridInqYbounds(gridID1, ybounds.data());
1039           if (check_range(2 * ny, ybounds.data(), -180, 180))
1040             {
1041               cdo_warning("latitude bounds out of range, skipped!");
1042               ybounds.clear();
1043             }
1044         }
1045       else if (ny > 1)
1046         {
1047           ybounds.resize(2 * ny);
1048           if (lprojection || projection == Projection::proj_params)
1049             grid_gen_bounds(ny, yvals, ybounds);
1050           else
1051             {
1052               grid_gen_bounds(ny, yvals, ybounds);
1053               grid_check_lat_borders(2 * ny, ybounds.data());
1054             }
1055         }
1056 
1057       if (xbounds.size() && ybounds.size())
1058         {
1059           std::vector<double> xbounds2D(4 * gridsize), ybounds2D(4 * gridsize);
1060 
1061           if (projection == Projection::proj_rll)
1062             {
1063               gridGenBoundsRLL(gridID1, nx, ny, xbounds, ybounds, xbounds2D, ybounds2D);
1064             }
1065           else
1066             {
1067               if (lprojection || projection == Projection::proj_params)
1068                 {
1069                   bounds_1D_to_2D(nx, ny, xbounds, ybounds, xbounds2D, ybounds2D, xscale, yscale);
1070                   if (projection != Projection::none)
1071                     apply_projection(projection, proj_params, gridID1, 4 * gridsize, xbounds2D.data(), ybounds2D.data());
1072                 }
1073               else
1074                 {
1075                   grid_gen_xbounds2D(nx, ny, xbounds, xbounds2D);
1076                   grid_gen_ybounds2D(nx, ny, ybounds, ybounds2D);
1077                 }
1078             }
1079 
1080           gridDefXbounds(gridID2, xbounds2D.data());
1081           gridDefYbounds(gridID2, ybounds2D.data());
1082         }
1083     }
1084 
1085   gridCopyMask(gridID1, gridID2, gridsize);
1086 
1087   return gridID2;
1088 }
1089 
1090 int
gridToUnstructuredSelecton(int gridID1,size_t selectionSize,size_t * selectionIndexList,int nocoords,int nobounds)1091 gridToUnstructuredSelecton(int gridID1, size_t selectionSize, size_t *selectionIndexList, int nocoords, int nobounds)
1092 {
1093   // transform input grid into a unstructured Version if necessary
1094   auto unstructuredGridID = (GRID_UNSTRUCTURED == gridInqType(gridID1)) ? gridID1 : gridToUnstructured(gridID1, !nobounds);
1095 
1096   auto unstructuredGridSize = gridInqSize(unstructuredGridID);
1097 
1098   auto unstructuredSelectionGridID = gridCreate(GRID_UNSTRUCTURED, selectionSize);
1099 
1100   if (nocoords) return unstructuredSelectionGridID;
1101 
1102   // copy meta data of coordinates
1103   grid_copy_keys(unstructuredGridID, unstructuredSelectionGridID);
1104 
1105   // TODO: select bounds
1106 
1107   // copy relevant coordinate
1108   std::vector<double> xvalsUnstructured(unstructuredGridSize), yvalsUnstructured(unstructuredGridSize);
1109   gridInqXvals(unstructuredGridID, xvalsUnstructured.data());
1110   gridInqYvals(unstructuredGridID, yvalsUnstructured.data());
1111 
1112   gridDefXsize(unstructuredSelectionGridID, selectionSize);
1113   gridDefYsize(unstructuredSelectionGridID, selectionSize);
1114   std::vector<double> xvals(selectionSize), yvals(selectionSize);
1115 
1116   for (size_t i = 0; i < selectionSize; i++) xvals[i] = xvalsUnstructured[selectionIndexList[i]];
1117   for (size_t i = 0; i < selectionSize; i++) yvals[i] = yvalsUnstructured[selectionIndexList[i]];
1118 
1119   gridDefXvals(unstructuredSelectionGridID, xvals.data());
1120   gridDefYvals(unstructuredSelectionGridID, yvals.data());
1121 
1122   // copy bounds if requested
1123   if (!nobounds)
1124     {
1125       size_t nvertex = gridInqNvertex(unstructuredGridID);
1126       std::vector<double> xbounds(nvertex * selectionSize), ybounds(nvertex * selectionSize);
1127       std::vector<double> xboundsUnstructured(nvertex * unstructuredGridSize), yboundsUnstructured(nvertex * unstructuredGridSize);
1128       gridInqXbounds(unstructuredGridID, xboundsUnstructured.data());
1129       gridInqYbounds(unstructuredGridID, yboundsUnstructured.data());
1130       for (size_t i = 0; i < selectionSize; i++)
1131         {
1132           const auto offset = selectionIndexList[i] * nvertex;
1133           for (size_t k = 0; k < nvertex; k++) xbounds[i * nvertex + k] = xboundsUnstructured[offset + k];
1134           for (size_t k = 0; k < nvertex; k++) ybounds[i * nvertex + k] = yboundsUnstructured[offset + k];
1135         }
1136       gridDefNvertex(unstructuredSelectionGridID, nvertex);
1137       gridDefXbounds(unstructuredSelectionGridID, xbounds.data());
1138       gridDefYbounds(unstructuredSelectionGridID, ybounds.data());
1139     }
1140 
1141   return unstructuredSelectionGridID;
1142 }
1143 
1144 static void
gridToUnstructuredRegular(int gridID1,int gridID2,size_t gridsize,int lbounds,bool lproj_rll)1145 gridToUnstructuredRegular(int gridID1, int gridID2, size_t gridsize, int lbounds, bool lproj_rll)
1146 {
1147   cdiDefKeyString(gridID2, CDI_XAXIS, CDI_KEY_NAME, "lon");
1148   cdiDefKeyString(gridID2, CDI_YAXIS, CDI_KEY_NAME, "lat");
1149   cdiDefKeyString(gridID2, CDI_XAXIS, CDI_KEY_LONGNAME, "longitude");
1150   cdiDefKeyString(gridID2, CDI_YAXIS, CDI_KEY_LONGNAME, "latitude");
1151   cdiDefKeyString(gridID2, CDI_XAXIS, CDI_KEY_UNITS, "degrees_east");
1152   cdiDefKeyString(gridID2, CDI_YAXIS, CDI_KEY_UNITS, "degrees_north");
1153 
1154   gridDefNvertex(gridID2, 4);
1155 
1156   const auto nx = gridInqXsize(gridID1);
1157   const auto ny = gridInqYsize(gridID1);
1158 
1159   const double xscale = 1.0;
1160   const double yscale = 1.0;
1161 
1162   gridDefXsize(gridID2, gridsize);
1163   gridDefYsize(gridID2, gridsize);
1164 
1165   std::vector<double> xvals(nx, 0), yvals(ny, 0);
1166   if (gridInqXvals(gridID1, nullptr)) gridInqXvals(gridID1, xvals.data());
1167   if (gridInqXvals(gridID1, nullptr)) gridInqYvals(gridID1, yvals.data());
1168 
1169   {
1170     std::vector<double> xvals2D(gridsize), yvals2D(gridsize);
1171 
1172     if (lproj_rll)
1173       gridGenCenterRLL(gridID1, nx, ny, xvals, yvals, xvals2D, yvals2D);
1174     else
1175       center_1D_to_2D(nx, ny, xvals, yvals, xvals2D, yvals2D, xscale, yscale);
1176 
1177     gridDefXvals(gridID2, xvals2D.data());
1178     gridDefYvals(gridID2, yvals2D.data());
1179   }
1180 
1181   if (lbounds)
1182     {
1183       const size_t nvertex = (size_t) gridInqNvertex(gridID1);
1184       std::vector<double> xbounds, ybounds;
1185 
1186       if (nvertex == 2 && gridInqXbounds(gridID1, nullptr))
1187         {
1188           xbounds.resize(2 * nx);
1189           gridInqXbounds(gridID1, xbounds.data());
1190         }
1191       else if (nx > 1)
1192         {
1193           xbounds.resize(2 * nx);
1194           grid_gen_bounds(nx, xvals, xbounds);
1195         }
1196 
1197       if (nvertex == 2 && gridInqYbounds(gridID1, nullptr))
1198         {
1199           ybounds.resize(2 * ny);
1200           gridInqYbounds(gridID1, ybounds.data());
1201         }
1202       else if (ny > 1)
1203         {
1204           ybounds.resize(2 * ny);
1205           grid_gen_bounds(ny, yvals, ybounds);
1206           grid_check_lat_borders(2 * ny, ybounds.data());
1207         }
1208 
1209       if (xbounds.size() && ybounds.size())
1210         {
1211           std::vector<double> xbounds2D(4 * gridsize), ybounds2D(4 * gridsize);
1212 
1213           if (lproj_rll)
1214             {
1215               gridGenBoundsRLL(gridID1, nx, ny, xbounds, ybounds, xbounds2D, ybounds2D);
1216             }
1217           else
1218             {
1219               grid_gen_xbounds2D(nx, ny, xbounds, xbounds2D);
1220               grid_gen_ybounds2D(nx, ny, ybounds, ybounds2D);
1221             }
1222 
1223           gridDefXbounds(gridID2, xbounds2D.data());
1224           gridDefYbounds(gridID2, ybounds2D.data());
1225         }
1226     }
1227 
1228   gridCopyMask(gridID1, gridID2, gridsize);
1229 }
1230 
1231 static void
gridToUnstructuredGaussianReduced(int gridID1,int gridID2,size_t gridsize,int lbounds)1232 gridToUnstructuredGaussianReduced(int gridID1, int gridID2, size_t gridsize, int lbounds)
1233 {
1234   cdiDefKeyString(gridID2, CDI_XAXIS, CDI_KEY_NAME, "lon");
1235   cdiDefKeyString(gridID2, CDI_YAXIS, CDI_KEY_NAME, "lat");
1236   cdiDefKeyString(gridID2, CDI_XAXIS, CDI_KEY_LONGNAME, "longitude");
1237   cdiDefKeyString(gridID2, CDI_YAXIS, CDI_KEY_LONGNAME, "latitude");
1238   cdiDefKeyString(gridID2, CDI_XAXIS, CDI_KEY_UNITS, "degrees_east");
1239   cdiDefKeyString(gridID2, CDI_YAXIS, CDI_KEY_UNITS, "degrees_north");
1240 
1241   gridDefNvertex(gridID2, 4);
1242 
1243   const auto nlat = gridInqYsize(gridID1);
1244   std::vector<int> reducedPoints(nlat);
1245   gridInqReducedPoints(gridID1, reducedPoints.data());
1246 
1247   gridDefXsize(gridID2, gridsize);
1248   gridDefYsize(gridID2, gridsize);
1249 
1250   if (nlat == gridInqYvals(gridID1, nullptr))
1251     {
1252       std::vector<double> yvals2D(gridsize), yvals(nlat);
1253       gridInqYvals(gridID1, yvals.data());
1254 
1255       size_t ij = 0;
1256       for (size_t j = 0; j < nlat; ++j)
1257         {
1258           const size_t nlon = reducedPoints[j];
1259           for (size_t i = 0; i < nlon; ++i) yvals2D[ij++] = yvals[j];
1260         }
1261 
1262       gridDefYvals(gridID2, yvals2D.data());
1263     }
1264   else
1265     {
1266       cdo_abort("%s: latitude coordinates missing!", gridNamePtr(gridInqType(gridID1)));
1267     }
1268 
1269   std::vector<double> xvals(gridsize);
1270 
1271   if (gridsize == gridInqXvals(gridID1, nullptr))
1272     {
1273       gridInqXvals(gridID1, xvals.data());
1274     }
1275   else
1276     {
1277       size_t ij = 0;
1278       for (size_t j = 0; j < nlat; ++j)
1279         {
1280           const size_t nlon = reducedPoints[j];
1281           for (size_t i = 0; i < nlon; ++i) xvals[ij++] = i * 360. / nlon;
1282         }
1283     }
1284 
1285   gridDefXvals(gridID2, xvals.data());
1286 
1287   if (lbounds)
1288     {
1289       const auto nvertex = (size_t) gridInqNvertex(gridID1);
1290       std::vector<double> ybounds;
1291       if (nvertex == 2 && gridInqYbounds(gridID1, nullptr))
1292         {
1293           ybounds.resize(2 * nlat);
1294           gridInqYbounds(gridID1, ybounds.data());
1295         }
1296 
1297       if (ybounds.size())
1298         {
1299           std::vector<double> xbounds2D(4 * gridsize), ybounds2D(4 * gridsize);
1300 
1301           size_t ij = 0;
1302           for (size_t j = 0; j < nlat; ++j)
1303             {
1304               const size_t nlon = reducedPoints[j];
1305               for (size_t i = 0; i < nlon; ++i)
1306                 {
1307                   xbounds2D[ij + 0] = (i + .5) * 360. / nlon;
1308                   xbounds2D[ij + 1] = (i + .5) * 360. / nlon;
1309                   xbounds2D[ij + 2] = (i - .5) * 360. / nlon;
1310                   xbounds2D[ij + 3] = (i - .5) * 360. / nlon;
1311                   ybounds2D[ij + 0] = ybounds[j * 2];
1312                   ybounds2D[ij + 1] = ybounds[j * 2 + 1];
1313                   ybounds2D[ij + 2] = ybounds[j * 2 + 1];
1314                   ybounds2D[ij + 3] = ybounds[j * 2];
1315                   ij += 4;
1316                 }
1317             }
1318 
1319           gridDefXbounds(gridID2, xbounds2D.data());
1320           gridDefYbounds(gridID2, ybounds2D.data());
1321         }
1322     }
1323 
1324   gridCopyMask(gridID1, gridID2, gridsize);
1325 }
1326 
1327 static void
gridToUnstructuredGME(int gridID1,int gridID2,size_t gridsize,int lbounds)1328 gridToUnstructuredGME(int gridID1, int gridID2, size_t gridsize, int lbounds)
1329 {
1330   constexpr size_t nv = 6;
1331 
1332   int nd, ni, ni2, ni3;
1333   gridInqParamGME(gridID1, &nd, &ni, &ni2, &ni3);
1334 
1335   std::vector<int> imask(gridsize);
1336   std::vector<double> xvals(gridsize), yvals(gridsize);
1337   std::vector<double> xbounds, ybounds;
1338   if (lbounds) xbounds.resize(nv * gridsize);
1339   if (lbounds) ybounds.resize(nv * gridsize);
1340 
1341   gme_grid(lbounds, gridsize, xvals.data(), yvals.data(), xbounds.data(), ybounds.data(), imask.data(), ni, nd, ni2, ni3);
1342 
1343   for (size_t i = 0; i < gridsize; i++)
1344     {
1345       xvals[i] *= RAD2DEG;
1346       yvals[i] *= RAD2DEG;
1347 
1348       if (lbounds)
1349         for (size_t j = 0; j < nv; j++)
1350           {
1351             xbounds[i * nv + j] *= RAD2DEG;
1352             ybounds[i * nv + j] *= RAD2DEG;
1353           }
1354       // printf("%d %g %g\n", i, xvals[i], yvals[i]);
1355     }
1356 
1357   gridDefXsize(gridID2, gridsize);
1358   gridDefYsize(gridID2, gridsize);
1359 
1360   gridDefXvals(gridID2, xvals.data());
1361   gridDefYvals(gridID2, yvals.data());
1362 
1363   gridDefMaskGME(gridID2, imask.data());
1364 
1365   gridDefNvertex(gridID2, nv);
1366 
1367   if (lbounds) gridDefXbounds(gridID2, xbounds.data());
1368   if (lbounds) gridDefYbounds(gridID2, ybounds.data());
1369 
1370   cdiDefKeyString(gridID2, CDI_XAXIS, CDI_KEY_UNITS, "degrees_east");
1371   cdiDefKeyString(gridID2, CDI_YAXIS, CDI_KEY_UNITS, "degrees_north");
1372 
1373   gridCopyMask(gridID1, gridID2, gridsize);
1374 }
1375 
1376 int
gridToUnstructured(int gridID1,int lbounds)1377 gridToUnstructured(int gridID1, int lbounds)
1378 {
1379   auto gridID2 = CDI_UNDEFID;
1380   auto gridtype = gridInqType(gridID1);
1381   const auto gridsize = gridInqSize(gridID1);
1382 
1383   bool lproj_rll = false;
1384   if (gridtype == GRID_PROJECTION && gridInqProjType(gridID1) == CDI_PROJ_RLL)
1385     {
1386       gridtype = GRID_LONLAT;
1387       lproj_rll = true;
1388     }
1389 
1390   switch (gridtype)
1391     {
1392     case GRID_LONLAT:
1393     case GRID_GAUSSIAN:
1394       {
1395         gridID2 = gridCreate(GRID_UNSTRUCTURED, gridsize);
1396         gridToUnstructuredRegular(gridID1, gridID2, gridsize, lbounds, lproj_rll);
1397         break;
1398       }
1399     case GRID_GAUSSIAN_REDUCED:
1400       {
1401         gridID2 = gridCreate(GRID_UNSTRUCTURED, gridsize);
1402         gridToUnstructuredGaussianReduced(gridID1, gridID2, gridsize, lbounds);
1403         break;
1404       }
1405     case GRID_GME:
1406       {
1407         gridID2 = gridCreate(GRID_UNSTRUCTURED, gridsize);
1408         gridToUnstructuredGME(gridID1, gridID2, gridsize, lbounds);
1409         break;
1410       }
1411     case GRID_CURVILINEAR:
1412       {
1413         gridID2 = gridDuplicate(gridID1);
1414         gridChangeType(gridID2, GRID_UNSTRUCTURED);
1415         gridDefXsize(gridID2, gridsize);
1416         gridDefYsize(gridID2, gridsize);
1417         break;
1418       }
1419     default:
1420       {
1421         cdo_abort("Grid type >%s< unsupported!", gridNamePtr(gridtype));
1422         break;
1423       }
1424     }
1425 
1426   cdiDefKeyInt(gridID2, CDI_GLOBAL, CDI_KEY_DATATYPE, CDI_DATATYPE_FLT32);
1427 
1428   return gridID2;
1429 }
1430 
1431 int
gridCurvilinearToRegular(int gridID1)1432 gridCurvilinearToRegular(int gridID1)
1433 {
1434   int gridID2 = -1;
1435   bool lx = true, ly = true;
1436 
1437   const auto gridtype = gridInqType(gridID1);
1438   const auto gridsize = gridInqSize(gridID1);
1439 
1440   if (gridtype != GRID_CURVILINEAR) return gridID2;
1441 
1442   const auto nx = gridInqXsize(gridID1);
1443   const auto ny = gridInqYsize(gridID1);
1444 
1445   std::vector<double> xvals2D(gridsize), yvals2D(gridsize);
1446   gridInqXvals(gridID1, xvals2D.data());
1447   gridInqYvals(gridID1, yvals2D.data());
1448 
1449   std::vector<double> xvals(nx), yvals(ny);
1450 
1451   for (size_t i = 0; i < nx; i++) xvals[i] = xvals2D[i];
1452   for (size_t j = 0; j < ny; j++) yvals[j] = yvals2D[j * nx];
1453 
1454   for (size_t j = 1; j < ny; j++)
1455     for (size_t i = 0; i < nx; i++)
1456       {
1457         if (std::fabs(xvals[i] - xvals2D[j * nx + i]) > 1.e-6)
1458           {
1459             lx = false;
1460             j = ny;
1461             break;
1462           }
1463       }
1464 
1465   for (size_t i = 1; i < nx; i++)
1466     for (size_t j = 0; j < ny; j++)
1467       {
1468         if (std::fabs(yvals[j] - yvals2D[j * nx + i]) > 1.e-6)
1469           {
1470             ly = false;
1471             i = nx;
1472             break;
1473           }
1474       }
1475 
1476   if (lx && ly)
1477     {
1478       gridID2 = gridCreate(GRID_LONLAT, gridsize);
1479       gridDefXsize(gridID2, nx);
1480       gridDefYsize(gridID2, ny);
1481 
1482       // cdiDefKeyInt(gridID2, CDI_GLOBAL, CDI_KEY_DATATYPE, CDI_DATATYPE_FLT32);
1483 
1484       cdo_grid_to_degree(gridID2, CDI_XAXIS, nx, xvals.data(), "grid1 center lon");
1485       cdo_grid_to_degree(gridID2, CDI_YAXIS, ny, yvals.data(), "grid1 center lat");
1486 
1487       gridDefXvals(gridID2, xvals.data());
1488       gridDefYvals(gridID2, yvals.data());
1489     }
1490 
1491   return gridID2;
1492 }
1493 
1494 static int
gridGenWeights(int gridID,double * grid_area,double * grid_wgts)1495 gridGenWeights(int gridID, double *grid_area, double *grid_wgts)
1496 {
1497   int status = 0;
1498   std::vector<int> grid_mask;
1499 
1500   const auto gridtype = gridInqType(gridID);
1501   const auto gridsize = gridInqSize(gridID);
1502 
1503   if (gridtype == GRID_GME)
1504     {
1505       gridID = gridToUnstructured(gridID, 1);
1506       grid_mask.resize(gridsize);
1507       gridInqMaskGME(gridID, grid_mask.data());
1508     }
1509 
1510   double total_area = 0;
1511   int nvals = 0;
1512   for (size_t i = 0; i < gridsize; i++)
1513     {
1514       if (grid_mask.size())
1515         if (grid_mask[i] == 0) continue;
1516       total_area += grid_area[i];
1517       nvals++;
1518     }
1519 
1520   if (gridVerbose) cdo_print("Total area = %g", total_area);
1521 
1522   for (size_t i = 0; i < gridsize; i++)
1523     {
1524       if (grid_mask.size())
1525         if (grid_mask[i] == 0)
1526           {
1527             grid_wgts[i] = 0;
1528             continue;
1529           }
1530 
1531       grid_wgts[i] = grid_area[i] / total_area;
1532     }
1533 
1534   return status;
1535 }
1536 
1537 int
gridWeights(const int gridID,double * grid_wgts)1538 gridWeights(const int gridID, double *grid_wgts)
1539 {
1540   int w_status = 1;
1541   int a_status = 0;
1542 
1543   const auto gridsize = gridInqSize(gridID);
1544   std::vector<double> grid_area(gridsize);
1545 
1546   if (gridHasArea(gridID))
1547     {
1548       if (gridVerbose) cdo_print("Using existing grid cell area!");
1549       gridInqArea(gridID, grid_area.data());
1550     }
1551   else
1552     {
1553       const auto gridtype = gridInqType(gridID);
1554       if (gridProjIsSupported(gridID) || gridtype == GRID_LONLAT || gridtype == GRID_GAUSSIAN || gridtype == GRID_GME
1555           || gridtype == GRID_CURVILINEAR || gridtype == GRID_UNSTRUCTURED)
1556         {
1557           a_status = gridGenArea(gridID, grid_area.data());
1558           if (a_status != 0 && (gridtype == GRID_LONLAT || gridtype == GRID_GAUSSIAN))
1559             a_status = gridGenAreaReg2Dweights(gridID, grid_area.data());
1560         }
1561       else
1562         {
1563           a_status = 1;
1564         }
1565     }
1566 
1567   if (a_status == 0)
1568     {
1569       w_status = gridGenWeights(gridID, grid_area.data(), grid_wgts);
1570     }
1571   else
1572     {
1573       for (size_t i = 0; i < gridsize; ++i) grid_wgts[i] = 1. / gridsize;
1574     }
1575   /*
1576   for ( i = 0; i < gridsize; ++i )
1577     printf("weights: %d %d %d %g %g\n", a_status, w_status, i, grid_area[i],
1578   grid_wgts[i]);
1579   */
1580 
1581   return w_status;
1582 }
1583 
1584 bool
grid_is_distance_generic(int gridID)1585 grid_is_distance_generic(int gridID)
1586 {
1587   bool status = false;
1588 
1589   if (gridInqType(gridID) == GRID_GENERIC)
1590     {
1591       char xunits[CDI_MAX_NAME], yunits[CDI_MAX_NAME];
1592       int length = CDI_MAX_NAME;
1593       cdiInqKeyString(gridID, CDI_XAXIS, CDI_KEY_UNITS, xunits, &length);
1594       length = CDI_MAX_NAME;
1595       cdiInqKeyString(gridID, CDI_YAXIS, CDI_KEY_UNITS, yunits, &length);
1596 
1597       if (cdo_cmpstr(xunits, "m") && cdo_cmpstr(yunits, "m") && gridHasCoordinates(gridID)) status = true;
1598     }
1599 
1600   return status;
1601 }
1602 
1603 bool
is_point_grid(int gridID)1604 is_point_grid(int gridID)
1605 {
1606   const auto gridtype = gridInqType(gridID);
1607   const auto projtype = gridInqProjType(gridID);
1608 
1609   const auto lprojection = (gridtype == GRID_PROJECTION && projtype == CDI_PROJ_RLL)
1610                            || (gridtype == GRID_PROJECTION && projtype == CDI_PROJ_LCC)
1611                            || (gridtype == GRID_PROJECTION && projtype == CDI_PROJ_STERE);
1612 
1613   return (gridtype == GRID_LONLAT || gridtype == GRID_GAUSSIAN || gridtype == GRID_CURVILINEAR || lprojection
1614           || gridtype == GRID_UNSTRUCTURED || gridtype == GRID_GME);
1615 }
1616 
1617 static int
generate_full_grid(int gridID,bool withBounds)1618 generate_full_grid(int gridID, bool withBounds)
1619 {
1620   auto gridtype = gridInqType(gridID);
1621 
1622   if (gridtype == GRID_GME || gridtype == GRID_GAUSSIAN_REDUCED)
1623     {
1624       gridID = gridToUnstructured(gridID, withBounds);
1625       gridtype = GRID_UNSTRUCTURED;
1626     }
1627 
1628   if (gridtype != GRID_UNSTRUCTURED && gridtype != GRID_CURVILINEAR)
1629     {
1630       gridID = gridToCurvilinear(gridID, withBounds);
1631       gridtype = GRID_CURVILINEAR;
1632     }
1633 
1634   if (gridtype == GRID_UNSTRUCTURED && !gridHasCoordinates(gridID))
1635     {
1636       const auto reference = dereferenceGrid(gridID);
1637       if (reference.isValid) gridID = reference.gridID;
1638       if (reference.notFound) cdo_abort("Reference to source grid not found!");
1639     }
1640 
1641   return gridID;
1642 }
1643 
1644 int
generate_full_point_grid(int gridID)1645 generate_full_point_grid(int gridID)
1646 {
1647   constexpr bool withBounds = false;
1648   return generate_full_grid(gridID, withBounds);
1649 }
1650 
1651 int
generate_full_cell_grid(int gridID)1652 generate_full_cell_grid(int gridID)
1653 {
1654   constexpr bool withBounds = true;
1655   return generate_full_grid(gridID, withBounds);
1656 }
1657