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