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