OpenCPN Partial API docs
Loading...
Searching...
No Matches
grib_record.cpp
Go to the documentation of this file.
1/**********************************************************************
2zyGrib: meteorological GRIB file viewer
3Copyright (C) 2008 - Jacques Zaninetti - http://www.zygrib.org
4
5This program is free software: you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation, either version 3 of the License, or
8(at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program. If not, see <http://www.gnu.org/licenses/>.
17***********************************************************************/
18
25#include "wx/wxprec.h"
26
27#ifndef WX_PRECOMP
28#include "wx/wx.h"
29#endif
30
31// #include <stdlib.h>
32
33// #include <QDateTime>
34
35#include "grib_record.h"
36
37// interpolate two angles in range +- 180 or +-PI, with resulting angle in the
38// same range
39static double interp_angle(double a0, double a1, double d, double p) {
40 if (a0 - a1 > p)
41 a0 -= 2 * p;
42 else if (a1 - a0 > p)
43 a1 -= 2 * p;
44 double a = (1 - d) * a0 + d * a1;
45 if (a < (p == 180. ? 0. : -p)) a += 2 * p;
46 return a;
47}
48
49//-------------------------------------------------------------------------------
50void GribRecord::Print() const {
51 printf(
52 "%d: id_center=%d id_model=%d id_grid=%d data_type=%d level_type=%d "
53 "level_value=%d hr=%f\n",
55 (cur_date - ref_date) / 3600.0);
56}
57
58//-------------------------------------------------------------------------------
59// Constructeur de recopie
60//-------------------------------------------------------------------------------
62 *this = rec;
63 is_duplicated = true;
64 // recopie les champs de bits
65 if (rec.data != nullptr) {
66 int size = static_cast<int>(rec.Ni * rec.Nj);
67 this->data = new double[size];
68 for (int i = 0; i < size; i++) this->data[i] = rec.data[i];
69 }
70 if (rec.bms_bits != nullptr) {
71 int size = rec.bms_size;
72 this->bms_bits = new zuchar[size];
73 for (int i = 0; i < size; i++) this->bms_bits[i] = rec.bms_bits[i];
74 }
75}
76
77bool GribRecord::GetInterpolatedParameters(
78 const GribRecord &rec1, const GribRecord &rec2, double &La1, double &Lo1,
79 double &La2, double &Lo2, double &Di, double &Dj, int &im1, int &jm1,
80 int &im2, int &jm2, int &Ni, int &Nj, int &rec1offi, int &rec1offj,
81 int &rec2offi, int &rec2offj) {
82 if (!rec1.IsOk() || !rec2.IsOk()) return false;
83
84 /* make sure Dj both have same sign */
85 if (rec1.GetDj() * rec2.GetDj() <= 0) return false;
86
87 Di = wxMax(rec1.GetDi(), rec2.GetDi());
88 Dj = rec1.GetDj() > 0 ? wxMax(rec1.GetDj(), rec2.GetDj())
89 : wxMin(rec1.GetDj(), rec2.GetDj());
90
91 /* get overlapping region */
92 if (Dj > 0)
93 La1 = wxMax(rec1.La1, rec2.La1), La2 = wxMin(rec1.La2, rec2.La2);
94 else
95 La1 = wxMin(rec1.La1, rec2.La1), La2 = wxMax(rec1.La2, rec2.La2);
96
97 Lo1 = wxMax(rec1.Lo1, rec2.Lo1), Lo2 = wxMin(rec1.Lo2, rec2.Lo2);
98
99 // align gribs on integer boundaries
100 int i, j;
101 // shut up compiler warning 'may be used uninitialized'
102 // rec2.Dj / rec1.Dj > 0
103 // XXX Is it true for rec2.Di / rec1.Di ?
104 double rec1offdi = 0, rec2offdi = 0;
105 double rec1offdj = 0., rec2offdj = 0.;
106
107 double iiters = rec2.Di / rec1.Di;
108 if (iiters < 1) {
109 iiters = 1 / iiters;
110 im1 = 1, im2 = static_cast<int>(iiters);
111 } else
112 im1 = static_cast<int>(iiters), im2 = 1;
113
114 for (i = 0; i < iiters; i++) {
115 rec1offdi = (Lo1 - rec1.Lo1) / rec1.Di;
116 rec2offdi = (Lo1 - rec2.Lo1) / rec2.Di;
117 if (rec1offdi == floor(rec1offdi) && rec2offdi == floor(rec2offdi)) break;
118
119 Lo1 += wxMin(rec1.Di, rec2.Di);
120 }
121 if (i == iiters) // failed to align, would need spacial interpolation to work
122 return false;
123
124 double jiters = rec2.Dj / rec1.Dj;
125 if (jiters < 1) {
126 jiters = 1 / jiters;
127 jm1 = 1, jm2 = static_cast<int>(jiters);
128 } else
129 jm1 = static_cast<int>(jiters), jm2 = 1;
130
131 for (j = 0; j < jiters; j++) {
132 rec1offdj = (La1 - rec1.La1) / rec1.Dj;
133 rec2offdj = (La1 - rec2.La1) / rec2.Dj;
134 if (rec1offdj == floor(rec1offdj) && rec2offdj == floor(rec2offdj)) break;
135
136 La1 += Dj < 0 ? wxMax(rec1.GetDj(), rec2.GetDj())
137 : wxMin(rec1.GetDj(), rec2.GetDj());
138 }
139 if (j == jiters) // failed to align
140 return false;
141
142 /* no overlap */
143 if (La1 * Dj > La2 * Dj || Lo1 > Lo2) return false;
144
145 /* compute integer sizes for data array */
146 Ni = (Lo2 - Lo1) / Di + 1, Nj = (La2 - La1) / Dj + 1;
147
148 /* back-compute final La2 and Lo2 to fit this integer boundary */
149 Lo2 = Lo1 + (Ni - 1) * Di, La2 = La1 + (Nj - 1) * Dj;
150
151 rec1offi = rec1offdi, rec2offi = static_cast<int>(rec2offdi);
152 rec1offj = static_cast<int>(rec1offdj),
153 rec2offj = static_cast<int>(rec2offdj);
154
155 if (!rec1.data || !rec2.data) return false;
156
157 return true;
158}
159
160//-------------------------------------------------------------------------------
161// Constructeur de interpolate
162//-------------------------------------------------------------------------------
164 const GribRecord &rec2, double d,
165 bool dir) {
166 double La1, Lo1, La2, Lo2, Di, Dj;
167 int im1, jm1, im2, jm2;
168 int Ni, Nj, rec1offi, rec1offj, rec2offi, rec2offj;
169 if (!GetInterpolatedParameters(rec1, rec2, La1, Lo1, La2, Lo2, Di, Dj, im1,
170 jm1, im2, jm2, Ni, Nj, rec1offi, rec1offj,
171 rec2offi, rec2offj))
172 return nullptr;
173
174 // recopie les champs de bits
175 int size = Ni * Nj;
176 auto *data = new double[size];
177
178 zuchar *BMSbits = nullptr;
179 if (rec1.bms_bits != nullptr && rec2.bms_bits != nullptr)
180 BMSbits = new zuchar[(Ni * Nj - 1) / 8 + 1]();
181
182 for (int i = 0; i < Ni; i++)
183 for (int j = 0; j < Nj; j++) {
184 int in = j * Ni + i;
185 int i1 = (j * jm1 + rec1offj) * rec1.Ni + i * im1 + rec1offi;
186 int i2 = (j * jm2 + rec2offj) * rec2.Ni + i * im2 + rec2offi;
187 double data1 = rec1.data[i1], data2 = rec2.data[i2];
188 if (data1 == GRIB_NOTDEF || data2 == GRIB_NOTDEF)
189 data[in] = GRIB_NOTDEF;
190 else {
191 if (!dir)
192 data[in] = (1 - d) * data1 + d * data2;
193 else
194 data[in] = interp_angle(data1, data2, d, 180.);
195 }
196
197 if (BMSbits) {
198 int b1 = rec1.bms_bits[i1 >> 3] & 1 << (i1 & 7);
199 int b2 = rec2.bms_bits[i2 >> 3] & 1 << (i2 & 7);
200 if (b1 && b2)
201 BMSbits[in >> 3] |= 1 << (in & 7);
202 else
203 BMSbits[in >> 3] &= ~(1 << (in & 7));
204 }
205 }
206
207 /* should maybe update str_cur_date ? */
208
209 GribRecord *ret = new GribRecord;
210 *ret = rec1;
211
212 ret->Di = Di, ret->Dj = Dj;
213 ret->Ni = Ni, ret->Nj = Nj;
214
215 ret->La1 = La1, ret->La2 = La2;
216 ret->Lo1 = Lo1, ret->Lo2 = Lo2;
217
218 ret->data = data;
219 ret->bms_bits = BMSbits;
220
221 ret->lat_min = wxMin(La1, La2), ret->lat_max = wxMax(La1, La2);
222 ret->lon_min = Lo1, ret->lon_max = Lo2;
223
224 ret->is_filled = false;
225
226 return ret;
227}
228
229/* for interpolation for x and y records, we must do them together because
230 otherwise we end up with a vector interpolation which is not what we want..
231 instead we want to interpolate from the polar magnitude, and angles */
233 GribRecord *&rety, const GribRecord &rec1x, const GribRecord &rec1y,
234 const GribRecord &rec2x, const GribRecord &rec2y, double d) {
235 double La1, Lo1, La2, Lo2, Di, Dj;
236 int im1, jm1, im2, jm2;
237 int Ni, Nj, rec1offi, rec1offj, rec2offi, rec2offj;
238
239 rety = nullptr;
240 if (!GetInterpolatedParameters(rec1x, rec2x, La1, Lo1, La2, Lo2, Di, Dj, im1,
241 jm1, im2, jm2, Ni, Nj, rec1offi, rec1offj,
242 rec2offi, rec2offj))
243 return nullptr;
244
245 if (!rec1y.data || !rec2y.data || !rec1y.IsOk() || !rec2y.IsOk() ||
246 rec1x.Di != rec1y.Di || rec1x.Dj != rec1y.Dj || rec2x.Di != rec2y.Di ||
247 rec2x.Dj != rec2y.Dj || rec1x.Ni != rec1y.Ni || rec1x.Nj != rec1y.Nj ||
248 rec2x.Ni != rec2y.Ni || rec2x.Nj != rec2y.Nj) {
249 // could also make sure lat and lon min/max are the same...
250 // copy first
251 rety = new GribRecord(rec1y);
252
253 return new GribRecord(rec1x);
254 }
255 // recopie les champs de bits
256 int size = Ni * Nj;
257 double *datax = new double[size], *datay = new double[size];
258 for (int i = 0; i < Ni; i++) {
259 for (int j = 0; j < Nj; j++) {
260 int in = j * Ni + i;
261 int i1 = (j * jm1 + rec1offj) * rec1x.Ni + i * im1 + rec1offi;
262 int i2 = (j * jm2 + rec2offj) * rec2x.Ni + i * im2 + rec2offi;
263 double data1x = rec1x.data[i1], data1y = rec1y.data[i1];
264 double data2x = rec2x.data[i2], data2y = rec2y.data[i2];
265 if (data1x == GRIB_NOTDEF || data1y == GRIB_NOTDEF ||
266 data2x == GRIB_NOTDEF || data2y == GRIB_NOTDEF) {
267 datax[in] = GRIB_NOTDEF;
268 datay[in] = GRIB_NOTDEF;
269 } else {
270 double data1m = sqrt(pow(data1x, 2) + pow(data1y, 2));
271 double data2m = sqrt(pow(data2x, 2) + pow(data2y, 2));
272 double datam = (1 - d) * data1m + d * data2m;
273
274 double data1a = atan2(data1y, data1x);
275 double data2a = atan2(data2y, data2x);
276 if (data1a - data2a > M_PI)
277 data1a -= 2 * M_PI;
278 else if (data2a - data1a > M_PI)
279 data2a -= 2 * M_PI;
280 double dataa = (1 - d) * data1a + d * data2a;
281
282 datax[in] = datam * cos(dataa);
283 datay[in] = datam * sin(dataa);
284 }
285 }
286 }
287
288 /* should maybe update str_cur_date ? */
289
290 GribRecord *ret = new GribRecord;
291
292 *ret = rec1x;
293
294 ret->Di = Di, ret->Dj = Dj;
295 ret->Ni = Ni, ret->Nj = Nj;
296
297 ret->La1 = La1, ret->La2 = La2;
298 ret->Lo1 = Lo1, ret->Lo2 = Lo2;
299
300 ret->data = datax;
301 ret->bms_bits = nullptr;
302 ret->hasBMS = false; // I don't think wind or current ever use BMS correct?
303
304 ret->lat_min = wxMin(La1, La2), ret->lat_max = wxMax(La1, La2);
305 ret->lon_min = Lo1, ret->lon_max = Lo2;
306
307 rety = new GribRecord;
308 *rety = *ret;
309 rety->data_type = rec1y.data_type;
310 rety->data = datay;
311 rety->bms_bits = nullptr;
312 rety->hasBMS = false;
313
314 return ret;
315}
316
317GribRecord *GribRecord::MagnitudeRecord(const GribRecord &rec1,
318 const GribRecord &rec2) {
319 GribRecord *rec = new GribRecord(rec1);
320
321 /* generate a record which is the combined magnitude of two records */
322 if (rec1.data && rec2.data && rec1.Ni == rec2.Ni && rec1.Nj == rec2.Nj) {
323 int size = rec1.Ni * rec1.Nj;
324 for (int i = 0; i < size; i++)
325 if (rec1.data[i] == GRIB_NOTDEF || rec2.data[i] == GRIB_NOTDEF)
326 rec->data[i] = GRIB_NOTDEF;
327 else
328 rec->data[i] = sqrt(pow(rec1.data[i], 2) + pow(rec2.data[i], 2));
329 } else
330 rec->ok = false;
331
332 if (rec1.bms_bits != nullptr && rec2.bms_bits != nullptr) {
333 if (rec1.bms_size == rec2.bms_size) {
334 int size = rec1.bms_size;
335 for (int i = 0; i < size; i++)
336 rec->bms_bits[i] = rec1.bms_bits[i] & rec2.bms_bits[i];
337 } else
338 rec->ok = false;
339 }
340
341 return rec;
342}
343
345 if (pDIR->data && pSPEED->data && pDIR->Ni == pSPEED->Ni &&
346 pDIR->Nj == pSPEED->Nj) {
347 int size = pDIR->Ni * pDIR->Nj;
348 for (int i = 0; i < size; i++) {
349 if (pDIR->data[i] != GRIB_NOTDEF && pSPEED->data[i] != GRIB_NOTDEF) {
350 double dir = pDIR->data[i];
351 double speed = pSPEED->data[i];
352 pDIR->data[i] = -speed * sin(dir * M_PI / 180.);
353 pSPEED->data[i] = -speed * cos(dir * M_PI / 180.);
354 }
355 }
356 if (pDIR->data_type == GRB_WIND_DIR) {
357 pDIR->data_type = GRB_WIND_VX;
358 pSPEED->data_type = GRB_WIND_VY;
359 } else {
360 pDIR->data_type = GRB_UOGRD;
361 pSPEED->data_type = GRB_VOGRD;
362 }
363 }
364}
365
366void GribRecord::Substract(const GribRecord &rec, bool pos) {
367 // for now only substract records of same size
368 if (rec.data == nullptr || !rec.IsOk()) return;
369
370 if (data == nullptr || !IsOk()) return;
371
372 if (Ni != rec.Ni || Nj != rec.Nj) return;
373
374 zuint size = Ni * Nj;
375 for (zuint i = 0; i < size; i++) {
376 if (rec.data[i] == GRIB_NOTDEF) continue;
377 if (data[i] == GRIB_NOTDEF) {
378 data[i] = -rec.data[i];
379 if (bms_bits != nullptr) {
380 if (bms_size > i) {
381 bms_bits[i >> 3] |= 1 << (i & 7);
382 }
383 }
384 } else
385 data[i] -= rec.data[i];
386 if (data[i] < 0. && pos) {
387 // data type should be positive...
388 data[i] = 0.;
389 }
390 }
391}
392
393//------------------------------------------------------------------------------
394void GribRecord::Average(const GribRecord &rec) {
395 // for now only average records of same size
396 // this : 6-12
397 // rec : 6-9
398 // compute average 9-12
399 //
400 // this : 0-12
401 // rec : 0-11
402 // compute average 11-12
403
404 if (rec.data == nullptr || !rec.IsOk()) return;
405
406 if (data == nullptr || !IsOk()) return;
407
408 if (Ni != rec.Ni || Nj != rec.Nj) return;
409
410 if (GetPeriodP1() != rec.GetPeriodP1()) return;
411
412 double d2 = GetPeriodP2() - GetPeriodP1();
413 double d1 = rec.GetPeriodP2() - rec.GetPeriodP1();
414
415 if (d2 <= d1) return;
416
417 zuint size = Ni * Nj;
418 double diff = d2 - d1;
419 for (zuint i = 0; i < size; i++) {
420 if (rec.data[i] == GRIB_NOTDEF) continue;
421 if (data[i] == GRIB_NOTDEF) continue;
422
423 data[i] = (data[i] * d2 - rec.data[i] * d1) / diff;
424 }
425}
426
427//-------------------------------------------------------------------------------
428void GribRecord::SetDataType(const zuchar t) {
429 data_type = t;
431}
432//------------------------------------------------------------------------------
433std::string GribRecord::MakeKey(
434 int dataType, int levelType,
435 int levelValue) { // Make data type key sample:'11-100-850'
436 // char ktmp[32];
437 // wxSnprintf((wxChar *)ktmp, 32, "%d-%d-%d", data_type, level_type,
438 // level_value); return std::string(ktmp);
439
440 wxString k;
441 k.Printf("%d-%d-%d", dataType, levelType, levelValue);
442 return std::string(k.mb_str());
443}
444//-----------------------------------------
445GribRecord::~GribRecord() {
446 if (data) {
447 delete[] data;
448 data = nullptr;
449 }
450 if (bms_bits) {
451 delete[] bms_bits;
452 bms_bits = nullptr;
453 }
454
455 // if (data_type==GRB_TEMP) printf("record destroyed %s %d\n",
456 // data_key.mb_str(), (int)cur_date/3600);
457}
458
459//-------------------------------------------------------------------------------
460void GribRecord::multiplyAllData(double k) {
461 if (!data || !IsOk()) return;
462
463 for (zuint j = 0; j < Nj; j++) {
464 for (zuint i = 0; i < Ni; i++) {
465 if (IsDefined(i, j)) {
466 data[j * Ni + i] *= k;
467 }
468 }
469 }
470}
471
472//----------------------------------------------
473void GribRecord::SetRecordCurrentDate(time_t t) {
474 cur_date = t;
475
476 struct tm *date = gmtime(&t);
477
478 zuint year = date->tm_year + 1900;
479 zuint month = date->tm_mon + 1;
480 zuint day = date->tm_mday;
481 zuint hour = date->tm_hour;
482 zuint minute = date->tm_min;
483 sprintf(str_cur_date, "%04d-%02d-%02d %02d:%02d", year, month, day, hour,
484 minute);
485}
486
487//----------------------------------------------
488static bool isleapyear(zuint y) {
489 return ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0);
490}
491
492time_t GribRecord::MakeDate(zuint year, zuint month, zuint day, zuint hour,
493 zuint min, zuint sec) {
494 if (year < 1970 || year > 2200 || month < 1 || month > 12 || day < 1)
495 return -1;
496 time_t r = 0;
497
498 // TODO : optimize (precomputed data)
499 for (zuint y = 1970; y < year; y++) {
500 r += 365 * 24 * 3600;
501 if (isleapyear(y)) r += 24 * 3600;
502 }
503 for (zuint m = 1; m < month; m++) {
504 if (m == 2) {
505 r += 28 * 24 * 3600;
506 if (isleapyear(year)) r += 24 * 3600;
507 } else if (m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 ||
508 m == 12) {
509 r += 31 * 24 * 3600;
510 } else {
511 r += 30 * 24 * 3600;
512 }
513 }
514 r += (day - 1) * 24 * 3600;
515 r += hour * 3600;
516 r += min * 60;
517 r += sec;
518 return r;
519}
520
521//===============================================================================================
522
523double GribRecord::GetInterpolatedValue(double px, double py,
524 bool numericalInterpolation,
525 bool dir) const {
526 if (!ok || Di == 0 || Dj == 0) return GRIB_NOTDEF;
527
528 if (!IsPointInMap(px, py)) {
529 px += 360.0; // tour du monde à droite ?
530 if (!IsPointInMap(px, py)) {
531 px -= 2 * 360.0; // tour du monde à gauche ?
532 if (!IsPointInMap(px, py)) {
533 return GRIB_NOTDEF;
534 }
535 }
536 }
537 double pi, pj; // coord. in grid unit
538 pi = (px - Lo1) / Di;
539 pj = (py - La1) / Dj;
540
541 // 00 10 point is in a square
542 // 01 11
543 int i0 = (int)pi; // point 00
544 int j0 = (int)pj;
545
546 unsigned int i1 = pi + 1, j1 = pj + 1;
547
548 if (i1 >= Ni) i1 = i0;
549
550 if (j1 >= Nj) j1 = j0;
551
552 // distances to 00
553 double dx = pi - i0;
554 double dy = pj - j0;
555
556 if (!numericalInterpolation) {
557 if (dx >= 0.5) i0 = i1;
558 if (dy >= 0.5) j0 = j1;
559
560 return GetValue(i0, j0);
561 }
562
563 // bool h00,h01,h10,h11;
564 // int nbval = 0; // how many values in grid ?
565 // if ((h00=IsDefined(i0, j0)))
566 // nbval ++;
567 // if ((h10=IsDefined(i1, j0)))
568 // nbval ++;
569 // if ((h01=IsDefined(i0, j1)))
570 // nbval ++;
571 // if ((h11=IsDefined(i1, j1)))
572 // nbval ++;
573
574 int nbval = 0; // how many values in grid ?
575 if (GetValue(i0, j0) != GRIB_NOTDEF) nbval++;
576 if (GetValue(i1, j0) != GRIB_NOTDEF) nbval++;
577 if (GetValue(i0, j1) != GRIB_NOTDEF) nbval++;
578 if (GetValue(i1, j1) != GRIB_NOTDEF) nbval++;
579
580 if (nbval < 3) return GRIB_NOTDEF;
581
582 dx = (3.0 - 2.0 * dx) * dx * dx; // pseudo hermite interpolation
583 dy = (3.0 - 2.0 * dy) * dy * dy;
584
585 double xa, xb, xc, kx, ky;
586 // Triangle :
587 // xa xb
588 // xc
589 // kx = distance(xa,x)
590 // ky = distance(xa,y)
591 if (nbval == 4) {
592 double x00 = GetValue(i0, j0);
593 double x01 = GetValue(i0, j1);
594 double x10 = GetValue(i1, j0);
595 double x11 = GetValue(i1, j1);
596 if (!dir) {
597 double x1 = (1.0 - dx) * x00 + dx * x10;
598 double x2 = (1.0 - dx) * x01 + dx * x11;
599 return (1.0 - dy) * x1 + dy * x2;
600 } else {
601 double x1 = interp_angle(x00, x01, dx, 180.);
602 double x2 = interp_angle(x10, x11, dx, 180.);
603 return interp_angle(x1, x2, dy, 180.);
604 }
605 }
606
607 // interpolation with only three points is too hazardous for angles
608 if (dir) return GRIB_NOTDEF;
609
610 // here nbval==3, check the corner without data
611 if (GetValue(i0, j0) == GRIB_NOTDEF) {
612 // printf("! h00 %f %f\n", dx,dy);
613 xa = GetValue(i1, j1); // A = point 11
614 xb = GetValue(i0, j1); // B = point 01
615 xc = GetValue(i1, j0); // C = point 10
616 kx = 1 - dx;
617 ky = 1 - dy;
618 } else if (GetValue(i0, j1) == GRIB_NOTDEF) {
619 // printf("! h01 %f %f\n", dx,dy);
620 xa = GetValue(i1, j0); // A = point 10
621 xb = GetValue(i1, j1); // B = point 11
622 xc = GetValue(i0, j0); // C = point 00
623 kx = dy;
624 ky = 1 - dx;
625 } else if (GetValue(i1, j0) == GRIB_NOTDEF) {
626 // printf("! h10 %f %f\n", dx,dy);
627 xa = GetValue(i0, j1); // A = point 01
628 xb = GetValue(i0, j0); // B = point 00
629 xc = GetValue(i1, j1); // C = point 11
630 kx = 1 - dy;
631 ky = dx;
632 } else {
633 // printf("! h11 %f %f\n", dx,dy);
634 xa = GetValue(i0, j0); // A = point 00
635 xb = GetValue(i1, j0); // B = point 10
636 xc = GetValue(i0, j1); // C = point 01
637 kx = dx;
638 ky = dy;
639 }
640
641 double k = kx + ky;
642 if (k < 0 || k > 1) return GRIB_NOTDEF;
643
644 if (k == 0) return xa;
645
646 // axes interpolation
647 double vx = k * xb + (1 - k) * xa;
648 double vy = k * xc + (1 - k) * xa;
649 // diagonal interpolation
650 double k2 = kx / k;
651 return k2 * vx + (1 - k2) * vy;
652}
653
654bool GribRecord::GetInterpolatedValues(double &M, double &A,
655 const GribRecord *GRX,
656 const GribRecord *GRY, double px,
657 double py, bool numericalInterpolation) {
658 if (!GRX || !GRY) return false;
659
660 if (!GRX->ok || !GRY->ok || GRX->Di == 0 || GRX->Dj == 0) return false;
661
662 if (!GRX->IsPointInMap(px, py) || !GRY->IsPointInMap(px, py)) {
663 px += 360.0; // tour du monde à droite ?
664 if (!GRX->IsPointInMap(px, py) || !GRY->IsPointInMap(px, py)) {
665 px -= 2 * 360.0; // tour du monde à gauche ?
666 if (!GRX->IsPointInMap(px, py) || !GRY->IsPointInMap(px, py)) {
667 return false;
668 }
669 }
670 }
671 double pi, pj; // coord. in grid unit
672 pi = (px - GRX->Lo1) / GRX->Di;
673 pj = (py - GRX->La1) / GRX->Dj;
674
675 // 00 10 point is in a square
676 // 01 11
677 int i0 = (int)pi; // point 00
678 int j0 = (int)pj;
679
680 unsigned int i1 = pi + 1, j1 = pj + 1;
681 if (i1 >= GRX->Ni) i1 = i0;
682
683 if (j1 >= GRX->Nj) j1 = j0;
684
685 // distances to 00
686 double dx = pi - i0;
687 double dy = pj - j0;
688
689 if (!numericalInterpolation) {
690 double vx, vy;
691 if (dx >= 0.5) i0 = i1;
692 if (dy >= 0.5) j0 = j1;
693
694 vx = GRX->GetValue(i0, j0);
695 vy = GRY->GetValue(i0, j0);
696 if (vx == GRIB_NOTDEF || vy == GRIB_NOTDEF) return false;
697
698 M = sqrt(vx * vx + vy * vy);
699 A = atan2(-vx, -vy) * 180 / M_PI;
700 return true;
701 }
702
703 // bool h00,h01,h10,h11;
704 // int nbval = 0; // how many values in grid ?
705 // if ((h00=GRX->IsDefined(i0, j0) && GRX->IsDefined(i0, j0)))
706 // nbval ++;
707 // if ((h10=GRX->IsDefined(i1, j0) && GRY->IsDefined(i1, j0)))
708 // nbval ++;
709 // if ((h01=GRX->IsDefined(i0, j1) && GRY->IsDefined(i0, j1)))
710 // nbval ++;
711 // if ((h11=GRX->IsDefined(i1, j1) && GRY->IsDefined(i1, j1)))
712 // nbval ++;
713
714 int nbval = 0; // how many values in grid ?
715 if (GRY->GetValue(i0, j0) != GRIB_NOTDEF) nbval++;
716 if (GRY->GetValue(i1, j0) != GRIB_NOTDEF) nbval++;
717 if (GRY->GetValue(i0, j1) != GRIB_NOTDEF) nbval++;
718 if (GRY->GetValue(i1, j1) != GRIB_NOTDEF) nbval++;
719
720 if (nbval <= 3) return false;
721
722 nbval = 0; // how many values in grid ?
723 if (GRX->GetValue(i0, j0) != GRIB_NOTDEF) nbval++;
724 if (GRX->GetValue(i1, j0) != GRIB_NOTDEF) nbval++;
725 if (GRX->GetValue(i0, j1) != GRIB_NOTDEF) nbval++;
726 if (GRX->GetValue(i1, j1) != GRIB_NOTDEF) nbval++;
727
728 if (nbval <= 3) return false;
729
730 dx = (3.0 - 2.0 * dx) * dx * dx; // pseudo hermite interpolation
731 dy = (3.0 - 2.0 * dy) * dy * dy;
732
733 // Triangle :
734 // xa xb
735 // xc
736 // kx = distance(xa,x)
737 // ky = distance(xa,y)
738 if (nbval == 4) {
739 double x00x = GRX->GetValue(i0, j0), x00y = GRY->GetValue(i0, j0);
740 double x00m = sqrt(x00x * x00x + x00y * x00y), x00a = atan2(x00x, x00y);
741
742 double x01x = GRX->GetValue(i0, j1), x01y = GRY->GetValue(i0, j1);
743 double x01m = sqrt(x01x * x01x + x01y * x01y), x01a = atan2(x01x, x01y);
744
745 double x10x = GRX->GetValue(i1, j0), x10y = GRY->GetValue(i1, j0);
746 double x10m = sqrt(x10x * x10x + x10y * x10y), x10a = atan2(x10x, x10y);
747
748 double x11x = GRX->GetValue(i1, j1), x11y = GRY->GetValue(i1, j1);
749 double x11m = sqrt(x11x * x11x + x11y * x11y), x11a = atan2(x11x, x11y);
750
751 double x0m = (1 - dx) * x00m + dx * x10m,
752 x0a = interp_angle(x00a, x10a, dx, M_PI);
753
754 double x1m = (1 - dx) * x01m + dx * x11m,
755 x1a = interp_angle(x01a, x11a, dx, M_PI);
756
757 M = (1 - dy) * x0m + dy * x1m;
758 A = interp_angle(x0a, x1a, dy, M_PI);
759 A *= 180 / M_PI; // degrees
760 A += 180;
761
762 return true;
763 }
764
765 return false; // TODO: make this work in the cases of only 3 points
766#if 0
767 double xa, xb, xc, kx, ky;
768 // here nbval==3, check the corner without data
769 if (!h00) {
770 //printf("! h00 %f %f\n", dx,dy);
771 xa = GetValue(i1, j1); // A = point 11
772 xb = GetValue(i0, j1); // B = point 01
773 xc = GetValue(i1, j0); // C = point 10
774 kx = 1-dx;
775 ky = 1-dy;
776 }
777 else if (!h01) {
778 //printf("! h01 %f %f\n", dx,dy);
779 xa = GetValue(i1, j0); // A = point 10
780 xb = GetValue(i1, j1); // B = point 11
781 xc = GetValue(i0, j0); // C = point 00
782 kx = dy;
783 ky = 1-dx;
784 }
785 else if (!h10) {
786 //printf("! h10 %f %f\n", dx,dy);
787 xa = GetValue(i0, j1); // A = point 01
788 xb = GetValue(i0, j0); // B = point 00
789 xc = GetValue(i1, j1); // C = point 11
790 kx = 1-dy;
791 ky = dx;
792 }
793 else {
794 //printf("! h11 %f %f\n", dx,dy);
795 xa = GetValue(i0, j0); // A = point 00
796 xb = GetValue(i1, j0); // B = point 10
797 xc = GetValue(i0, j1); // C = point 01
798 kx = dx;
799 ky = dy;
800 }
801 }
802 double k = kx + ky;
803 if (k<0 || k>1) {
804 val = GRIB_NOTDEF;
805 }
806 else if (k == 0) {
807 val = xa;
808 }
809 else {
810 // axes interpolation
811 double vx = k*xb + (1-k)*xa;
812 double vy = k*xc + (1-k)*xa;
813 // diagonal interpolation
814 double k2 = kx / k;
815 val = k2*vx + (1-k2)*vy;
816 }
817 return val;
818#endif
819}
A meteorological data grid from a GRIB (Gridded Binary) file.
static bool GetInterpolatedValues(double &M, double &A, const GribRecord *GRX, const GribRecord *GRY, double px, double py, bool numericalInterpolation=true)
Gets spatially interpolated wind or current vector values at a specific latitude/longitude point.
static void Polar2UV(GribRecord *pDIR, GribRecord *pSPEED)
Converts wind or current values from polar (direction/speed) to cartesian (U/V) components.
time_t cur_date
Unix timestamp of when this forecast is valid.
bool is_duplicated
Indicates if this record was created through copying rather than direct reading.
double Lo2
Grid end coordinates.
GribRecord(const GribRecord &rec)
Copy constructor performs a deep copy of the GribRecord.
double Lo1
Grid origin coordinates.
bool is_filled
Indicates whether the data array has been populated.
static GribRecord * Interpolated2DRecord(GribRecord *&rety, const GribRecord &rec1x, const GribRecord &rec1y, const GribRecord &rec2x, const GribRecord &rec2y, double d)
Creates temporally interpolated records for vector fields (wind, currents).
int GetPeriodP2() const
Returns the end of the period (P2) used for this record.
zuchar id_model
Model identifier within the originating center.
double GetValue(int i, int j) const
Returns the data value at a specific grid point.
std::string data_key
Unique string identifier constructed from data type, level type, and level value.
static GribRecord * InterpolatedRecord(const GribRecord &rec1, const GribRecord &rec2, double d, bool dir=false)
Creates a new GribRecord by temporally interpolating between two time points.
zuchar id_grid
Grid identifier used by the originating center.
bool ok
Indicates record validity.
double GetInterpolatedValue(double px, double py, bool numericalInterpolation=true, bool dir=false) const
Get spatially interpolated value at exact lat/lon position.
bool hasBMS
Indicates presence of a bitmap section.
zuint level_value
Numeric value associated with level_type.
int GetPeriodP1() const
Returns the start of the period (P1) used for this record.
zuchar data_type
Parameter identifier as defined by GRIB tables.
double GetDj() const
Returns the grid spacing in latitude (j) direction in degrees.
time_t ref_date
Unix timestamp of model initialization time.
zuchar level_type
Vertical level type indicator.
zuchar id_center
Originating center ID as defined by WMO common table C-1.
double GetDi() const
Returns the grid spacing in longitude (i) direction in degrees.
GRIB Record Base Class Implementation.