IODA Bundle
EqRegionCache.cc
Go to the documentation of this file.
1 /*
2  * (C) Copyright 1996-2012 ECMWF.
3  *
4  * This software is licensed under the terms of the Apache Licence Version 2.0
5  * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6  * In applying this licence, ECMWF does not waive the privileges and immunities
7  * granted to it by virtue of its status as an intergovernmental organisation nor
8  * does it submit to any jurisdiction.
9  */
10 
11 #include <algorithm>
12 #include <cmath>
13 
14 #include "eckit/exception/Exceptions.h"
15 #include "eckit/log/Log.h"
16 
18 
19 using namespace eckit;
20 
21 namespace odc {
22 namespace sql {
23 namespace function {
24 
25 #define regions(i, j, k) regs[((k)-1) * dim * 2 + ((j)-1) * dim + (i)-1]
26 #define regions_1(i, j, k) regs_1[((k)-1) * (dim - 1) * 2 + ((j)-1) * (dim - 1) + (i)-1]
27 #define region(i, j) reg[((j)-1) * dim + (i)-1]
28 
29 extern double mfmod(double x, double y);
30 
31 
32 EqRegionCache::EqRegionCache() : RegionCache() {}
33 
34 
36 
37 
38 int EqRegionCache::gcd(int a, int& b)
39 {
40  int m = std::abs(a);
41  int n = std::abs(b);
42  if (m > n) {
43  std::swap(m, n);
44  }
45  return (m == 0) ? n : gcd(n % m, m);
46 }
47 
48 
49 void EqRegionCache::bot_cap_region(int& dim, double& a_cap, double reg[])
50 {
51  //
52  // An array of two points representing the bottom cap of radius a_cap as a region.
53  //
54  if (dim == 1) {
55  region(1, 1) = piconst::two_pi - a_cap;
56  region(1, 2) = piconst::two_pi;
57  }
58  else if (dim == 2) {
59  // sphere_region_1 = sphere_region(dim-1);
60  // region = [[sphere_region_1(:,1); pi-a_cap],[sphere_region_1(:,2); pi]];
61  region(1, 1) = 0;
62  region(1, 2) = piconst::two_pi;
63  region(2, 1) = piconst::pi - a_cap;
64  region(2, 2) = piconst::pi;
65  }
66  else {
67  Log::info() << "bot_cap_region: dim > 2 not supported";
68  }
69 }
70 
71 
72 double EqRegionCache::circle_offset(int& n_top, int& n_bot)
73 {
74  //
75  // CIRCLE_OFFSET Try to maximize minimum distance of center points for S^2 collars
76  //
77  // Given n_top and n_bot, calculate an offset.
78  //
79  // The values n_top and n_bot represent the numbers of
80  // equally spaced points on two overlapping circles.
81  // The offset is given in multiples of whole rotations, and
82  // consists of three parts;
83  // 1) Half the difference between a twist of one sector on each of bottom and top.
84  // This brings the centre points into alignment.
85  // 2) A rotation which will maximize the minimum angle between
86  // points on the two circles.
87  //
88  double co = (double)(1 / (double)(n_bot)-1 / (double)(n_top)) / 2 +
89  (double)(gcd(n_top, n_bot)) / (2 * (double)(n_top) * (double)(n_bot));
90  return co;
91 }
92 
93 
94 void EqRegionCache::cap_colats(int& dim, int& n, int& n_collars, double& c_polar,
95  const int n_regions[/* n_collars+2 */], double c_caps[/* n_collars+2 */])
96 {
97  //
98  // CAP_COLATS Colatitudes of spherical caps enclosing cumulative sum of regions
99  //
100  // Given dim, N, c_polar and n_regions, determine c_caps,
101  // an increasing list of colatitudes of spherical caps which enclose the same area
102  // as that given by the cumulative sum of regions.
103  // The number of elements is n_collars+2.
104  // c_caps[1] is c_polar.
105  // c_caps[n_collars+1] is Pi-c_polar.
106  // c_caps[n_collars+2] is Pi.
107  //
108 
109  int subtotal_n_regions, collar_n;
110  double ideal_region_area = area_of_ideal_region(dim, n);
111  c_caps[0] = c_polar;
112  subtotal_n_regions = 1;
113  double area;
114  for (collar_n = 1; collar_n <= n_collars; collar_n++) {
115  subtotal_n_regions += n_regions[collar_n];
116  area = subtotal_n_regions * ideal_region_area;
117  c_caps[collar_n] = sradius_of_cap(dim, area);
118  }
119  c_caps[n_collars + 1] = piconst::pi;
120 }
121 
122 
123 void EqRegionCache::round_to_naturals(int& n, int& n_collars, const double r_regions[/* n_collars+2 */],
124  int n_regions[/* n_collars+2 */])
125 {
126  //
127  // ROUND_TO_NATURALS Round off a given list of numbers of regions
128  //
129  // Given N and r_regions, determine n_regions,
130  // a list of the natural number of regions in each collar and the polar caps.
131  // This list is as close as possible to r_regions, using rounding.
132  // The number of elements is n_collars+2.
133  // n_regions[1] is 1.
134  // n_regions[n_collars+2] is 1.
135  // The sum of n_regions is N.
136  //
137  int j;
138  double discrepancy = 0;
139  for (j = 0; j < n_collars + 2; j++) {
140  n_regions[j] = NINT(r_regions[j] + discrepancy);
141  discrepancy += r_regions[j] - n_regions[j];
142  }
143 }
144 
145 double EqRegionCache::area_of_cap(int& dim, double& s_cap) {
146  // AREA_OF_CAP Area of spherical cap
147  //
148  // Syntax
149  // area = area_of_cap(dim, s_cap);
150  //
151  // Description
152  // AREA = AREA_OF_CAP(dim, S_CAP) sets AREA to be the area of an S^dim spherical
153  // cap of spherical radius S_CAP.
154  //
155  // The argument dim must be a positive integer.
156  // The argument S_CAP must be a real number or an array of real numbers.
157  // The result AREA will be an array of the same size as S_CAP.
158 
159  ASSERT(dim > 0);
160  double area = 0;
161  if (dim == 1) {
162  area = 2 * s_cap;
163  }
164  else if (dim == 2) {
165  area = piconst::four_pi * pow(sin(s_cap / 2), 2);
166  }
167  else {
168  Log::info() << "area_of_cap: dim > 2 not supported";
169  }
170  return area;
171 }
172 
173 
174 double EqRegionCache::area_of_collar(int& dim, double a_top, double a_bot)
175 {
176  //
177  // AREA_OF_COLLAR Area of spherical collar
178  //
179  // Syntax
180  // area = area_of_collar(dim, a_top, a_bot);
181  //
182  // Description
183  // AREA = AREA_OF_COLLAR(dim, A_TOP, A_BOT) sets AREA to be the area of
184  // an S^dim spherical collar specified by A_TOP, A_BOT, where
185  // A_TOP is top (smaller) spherical radius,
186  // A_BOT is bottom (larger) spherical radius.
187  //
188  // The argument dim must be a positive integer.
189  // The arguments A_TOP and A_BOT must be real numbers or arrays of real numbers,
190  // with the same array size.
191  // The result AREA will be an array of the same size as A_TOP.
192  //
193  return area_of_cap(dim, a_bot) - area_of_cap(dim, a_top);
194 }
195 
196 
197 void EqRegionCache::ideal_region_list(int& dim, int& n, double& c_polar, int& n_collars,
198  double r_regions[/* n_collars+2 */])
199 {
200  //
201  // IDEAL_REGION_LIST The ideal real number of regions in each zone
202  //
203  // List the ideal real number of regions in each collar, plus the polar caps.
204  //
205  // Given dim, N, c_polar and n_collars, determine r_regions,
206  // a list of the ideal real number of regions in each collar,
207  // plus the polar caps.
208  // The number of elements is n_collars+2.
209  // r_regions[1] is 1.
210  // r_regions[n_collars+2] is 1.
211  // The sum of r_regions is N.
212  //
213  r_regions[0] = 1;
214  if (n_collars > 0) {
215  //
216  // Based on n_collars and c_polar, determine a_fitting,
217  // the collar angle such that n_collars collars fit between the polar caps.
218  //
219  int collar_n;
220  double a_fitting = (piconst::pi - 2 * c_polar) / n_collars;
221  double ideal_region_area = area_of_ideal_region(dim, n);
222  for (collar_n = 1; collar_n <= n_collars; collar_n++) {
223  double ideal_collar_area =
224  area_of_collar(dim, c_polar + (collar_n - 1) * a_fitting, c_polar + collar_n * a_fitting);
225  r_regions[collar_n] = ideal_collar_area / ideal_region_area;
226  }
227  } /* if ( n_collars > 0 ) */
228  r_regions[n_collars + 1] = 1;
229 }
230 
231 
233 {
234  //
235  // AREA = AREA_OF_IDEAL_REGION(dim,N) sets AREA to be the area of one of N equal
236  // area regions on S^dim, that is 1/N times AREA_OF_SPHERE(dim).
237  //
238  // The argument dim must be a positive integer.
239  // The argument N must be a positive integer or an array of positive integers.
240  // The result AREA will be an array of the same size as N.
241  //
242  double area = area_of_sphere(dim) / n;
243  return area;
244 }
245 
246 
247 int EqRegionCache::num_collars(int& n, double& c_polar, double a_ideal)
248 {
249  //
250  // NUM_COLLARS The number of collars between the polar caps
251  //
252  // Given N, an ideal angle, and c_polar,
253  // determine n_collars, the number of collars between the polar caps.
254  //
255  int num_c;
256  // n_collars = zeros(size(N));
257  // enough = (N > 2) & (a_ideal > 0);
258  // n_collars(enough) = max(1,round((pi-2*c_polar(enough))./a_ideal(enough)));
259  bool enough = ((n > 2) && (a_ideal > 0)) ? true : false;
260  if (enough) {
261  num_c = NINT((piconst::pi - 2 * c_polar) / a_ideal);
262  if (num_c < 1)
263  num_c = 1;
264  }
265  else {
266  num_c = 0;
267  }
268  return num_c;
269 }
270 
271 
273 {
274  //
275  // IDEAL_COLLAR_ANGLE The ideal angle for spherical collars of an EQ partition
276  //
277  // Syntax
278  // angle = ideal_collar_angle(dim,N);
279  //
280  // Description
281  // ANGLE = IDEAL_COLLAR_ANGLE(dim,N) sets ANGLE to the ideal angle for the
282  // spherical collars of an EQ partition of the unit sphere S^dim into N regions.
283  //
284  // The argument dim must be a positive integer.
285  // The argument N must be a positive integer or an array of positive integers.
286  // The result ANGLE will be an array of the same size as N.
287  //
288  return pow(area_of_ideal_region(dim, n), (1 / (double)(dim)));
289 }
290 
291 
292 double EqRegionCache::my_gamma(double& x)
293 {
294  const double p0 = 0.999999999999999990e0;
295  const double p1 = -0.422784335098466784e0;
296  const double p2 = -0.233093736421782878e0;
297  const double p3 = 0.191091101387638410e0;
298  const double p4 = -0.024552490005641278e0;
299  const double p5 = -0.017645244547851414e0;
300  const double p6 = 0.008023273027855346e0;
301  const double p7 = -0.000804329819255744e0;
302  const double p8 = -0.000360837876648255e0;
303  const double p9 = 0.000145596568617526e0;
304  const double p10 = -0.000017545539395205e0;
305  const double p11 = -0.000002591225267689e0;
306  const double p12 = 0.000001337767384067e0;
307  const double p13 = -0.000000199542863674e0;
308  int n = NINT(x - 2);
309  double w = x - (n + 2);
310  double y =
311  ((((((((((((p13 * w + p12) * w + p11) * w + p10) * w + p9) * w + p8) * w + p7) * w + p6) * w + p5) * w + p4) *
312  w +
313  p3) *
314  w +
315  p2) *
316  w +
317  p1) *
318  w +
319  p0;
320  int k;
321  if (n > 0) {
322  w = x - 1;
323  for (k = 2; k <= n; k++)
324  w *= (x - k);
325  }
326  else {
327  int nn = -n - 1;
328  w = 1;
329  for (k = 0; k <= nn; k++)
330  y *= (x + k);
331  }
332  return w / y;
333 }
334 
335 
337 {
338  //
339  // AREA = AREA_OF_SPHERE(dim) sets AREA to be the area of the sphere S^dim
340  //
341  double power = (double)(dim + 1) / (double)2;
342  return 2 * pow(piconst::pi, power) / my_gamma(power);
343 }
344 
345 
346 double EqRegionCache::sradius_of_cap(int& dim, double& area)
347 {
348  //
349  // S_CAP = SRADIUS_OF_CAP(dim, AREA) returns the spherical radius of
350  // an S^dim spherical cap of area AREA.
351  //
352  double radius = 0;
353  if (dim == 1) {
354  radius = area / 2;
355  }
356  else if (dim == 2) {
357  radius = 2 * asin(sqrt(area / piconst::pi) / 2);
358  }
359  else {
360  Log::info() << "sradius_of_cap: dim > 2 not supported";
361  }
362  return radius;
363 }
364 
365 
366 double EqRegionCache::polar_colat(int& dim, int& n)
367 
368 {
369  //
370  // Given dim and N, determine the colatitude of the North polar spherical cap.
371  //
372  ASSERT(n > 0);
373  double colat = 0;
374  if (n == 1)
375  colat = piconst::pi;
376  else if (n == 2)
377  colat = piconst::half_pi;
378  else if (n > 2) {
379  double area = area_of_ideal_region(dim, n);
380  colat = sradius_of_cap(dim, area);
381  }
382  return colat;
383 }
384 
385 
386 void EqRegionCache::top_cap_region(int& dim, double& a_cap, double reg[])
387 
388 {
389  //
390  // An array of two points representing the top cap of radius a_cap as a region.
391  //
392  if (dim == 1) {
393  region(1, 1) = 0;
394  region(1, 2) = a_cap;
395  }
396  else if (dim == 2) {
397  // sphere_region_1 = sphere_region(dim-1);
398  // region = [[sphere_region_1(:,1); 0], [sphere_region_1(:,2); a_cap]];
399  region(1, 1) = 0;
400  region(1, 2) = piconst::two_pi;
401  region(2, 1) = 0;
402  region(2, 2) = a_cap;
403  }
404  else {
405  Log::info() << "top_cap_region: dim > 2 not supported";
406  }
407 }
408 
409 
410 void EqRegionCache::sphere_region(int& dim, double reg[])
411 
412 {
413  //
414  // An array of two points representing S^dim as a region.
415  //
416  if (dim == 1) {
417  region(1, 1) = 0;
418  region(1, 2) = piconst::two_pi;
419  }
420  else if (dim == 2) {
421  //
422  // sphere_region_1 = sphere_region(dim-1);
423  // region = [[sphere_region_1(:,1); 0],[sphere_region_1(:,2); pi]] ;
424  //
425  region(1, 1) = 0;
426  region(1, 2) = piconst::two_pi;
427  region(2, 1) = 0;
428  region(2, 2) = piconst::pi;
429  }
430  else {
431  Log::info() << "sphere_region: dim > 2 not supported";
432  }
433 }
434 
435 
436 void EqRegionCache::eq_caps(int& dim, int& n, double s_cap[/* n */], int n_regions[/* n */], int* N_collars)
437 
438 {
439  int j;
440  double c_polar;
441  int n_collars = *N_collars;
442 
443  if (n == 1) {
444  //
445  // We have only one region, which must be the whole sphere.
446  //
447  s_cap[0] = piconst::pi;
448  n_regions[0] = 1;
449  *N_collars = 0;
450  return;
451  }
452 
453  if (dim == 1) {
454  //
455  // We have a circle. Return the angles of N equal sectors.
456  //
457  // sector = 1:N;
458  //
459  // Make dim==1 consistent with dim>1 by
460  // returning the longitude of a sector enclosing the
461  // cumulative sum of arc lengths given by summing n_regions.
462  //
463  // s_cap = sector*two_pi/N;
464  // n_regions = ones(size(sector));
465  for (j = 0; j < n; j++) {
466  s_cap[j] = (j + 1) * piconst::two_pi / (double)n;
467  n_regions[j] = 1;
468  }
469  *N_collars = 0;
470  return;
471  }
472 
473  if (dim == 2) {
474  //
475  // Given dim and N, determine c_polar
476  // the colatitude of the North polar spherical cap.
477  //
478 
479  c_polar = polar_colat(dim, n);
480 
481  //
482  // Given dim and N, determine the ideal angle for spherical collars.
483  // Based on N, this ideal angle, and c_polar,
484  // determine n_collars, the number of collars between the polar caps.
485  //
486 
487  n_collars = *N_collars = num_collars(n, c_polar, ideal_collar_angle(dim, n));
488 
489  //
490  // Given dim, N, c_polar and n_collars, determine r_regions,
491  // a list of the ideal real number of regions in each collar,
492  // plus the polar caps.
493  // The number of elements is n_collars+2.
494  // r_regions[1] is 1.
495  // r_regions[n_collars+2] is 1.
496  // The sum of r_regions is N.
497  //
498  // r_regions = ideal_region_list(dim,N,c_polar,n_collars)
499 
500  {
501  double* r_regions = NULL;
502  r_regions = new double[n_collars + 2];
503  ideal_region_list(dim, n, c_polar, n_collars, r_regions);
504 
505  //
506  // Given N and r_regions, determine n_regions,
507  // a list of the natural number of regions in each collar and
508  // the polar caps.
509  // This list is as close as possible to r_regions.
510  // The number of elements is n_collars+2.
511  // n_regions[1] is 1.
512  // n_regions[n_collars+2] is 1.
513  // The sum of n_regions is N.
514  //
515  // n_regions = round_to_naturals(N,r_regions)
516 
517  round_to_naturals(n, n_collars, r_regions, n_regions);
518  delete[] r_regions;
519  }
520 
521  //
522  // Given dim, N, c_polar and n_regions, determine s_cap,
523  // an increasing list of colatitudes of spherical caps which enclose the same area
524  // as that given by the cumulative sum of regions.
525  // The number of elements is n_collars+2.
526  // s_cap[1] is c_polar.
527  // s_cap[n_collars+1] is Pi-c_polar.
528  // s_cap[n_collars+2] is Pi
529  //
530  // s_cap = cap_colats(dim,N,c_polar,n_regions)
531 
532  cap_colats(dim, n, n_collars, c_polar, n_regions, s_cap);
533  }
534 }
535 
536 
537 void EqRegionCache::eq_regions(int dim, int n, double regs[])
538 
539 {
540  //
541  //
542  // REGIONS = EQ_REGIONS(dim,N) uses the recursive zonal equal area sphere
543  // partitioning algorithm to partition S^dim (the unit sphere in dim+1
544  // dimensional space) into N regions of equal area and small diameter.
545  //
546  // The arguments dim and N must be positive integers.
547  //
548  // The result REGIONS is a (dim by 2 by N) array, representing the regions
549  // of S^dim. Each element represents a pair of vertex points in spherical polar
550  // coordinates.
551  //
552  // Each region is defined as a product of intervals in spherical polar
553  // coordinates. The pair of vertex points regions(:,1,n) and regions(:,2,n) give
554  // the lower and upper limits of each interval.
555 
556  double* regs_1 = NULL;
557  double *s_cap = NULL, r_top[2], r_bot[2], c_top, c_bot, offset;
558  int *n_regions = NULL, collar_n, n_collars, n_in_collar, region_1_n, region_n;
559  int i, j, k;
560 
561  s_cap = new double[n + 2];
562  n_regions = new int[n + 2];
563 
564  if (n == 1) {
565  //
566  //
567  // We have only one region, which must be the whole sphere.
568  //
569  // regions(:,:,1)=sphere_region(dim)
570  //
571  sphere_region(dim, &regions(1, 1, 1));
572  }
573  else {
574  //
575  //
576  // Start the partition of the sphere into N regions by partitioning
577  // to caps defined in the current dimension.
578  //
579  // [s_cap, n_regions] = eq_caps(dim,N)
580  //
581 
582 
583  eq_caps(dim, n, s_cap, n_regions, &n_collars);
584 
585  //
586  //
587  // s_cap is an increasing list of colatitudes of the caps.
588  //
589  //
590  if (dim == 1) {
591  //
592  //
593  // We have a circle and s_cap is an increasing list of angles of sectors.
594  //
595  //
596  // Return a list of pairs of sector angles.
597  //
598  //
599  for (k = 1; k <= n; k++)
600  for (j = 1; j <= 2; j++)
601  for (i = 1; i <= dim; i++)
602  regions(i, j, k) = 0;
603 
604  for (k = 2; k <= n; k++)
605  regions(1, 1, k) = s_cap[k - 2];
606 
607  for (k = 1; k <= n; k++)
608  regions(1, 2, k) = s_cap[k - 1];
609  }
610  else {
611 
612  //
613  // We have a number of zones: two polar caps and a number of collars.
614  // n_regions is the list of the number of regions in each zone.
615  //
616  // n_collars = size(n_regions,2)-2
617  //
618  // Start with the top cap
619  //
620  // regions(:,:,1) = top_cap_region(dim,s_cap(1))
621  //
622 
623  top_cap_region(dim, s_cap[0], &regions(1, 1, 1));
624  region_n = 1;
625 
626  //
627  //
628  // Determine the dim-regions for each collar
629  //
630  //
631  if (dim == 2)
632  offset = 0;
633  for (collar_n = 1; collar_n <= n_collars; collar_n++) {
634  int size_regions_1_3;
635  //
636  // c_top is the colatitude of the top of the current collar.
637  //
638  c_top = s_cap[collar_n - 1];
639 
640  //
641  // c_bot is the colatitude of the bottom of the current collar.
642  //
643  c_bot = s_cap[collar_n];
644 
645  //
646  // n_in_collar is the number of regions in the current collar.
647  //
648  n_in_collar = n_regions[collar_n];
649 
650  //
651  // The top and bottom of the collar are small (dim-1)-spheres,
652  // which must be partitioned into n_in_collar regions.
653  // Use eq_regions recursively to partition the unit (dim-1)-sphere.
654  // regions_1 is the resulting list of (dim-1)-region pairs.
655  //
656  // regions_1 = eq_regions(dim-1,n_in_collar)
657  //
658  size_regions_1_3 = n_in_collar;
659 
660  regs_1 = new double[(dim - 1) * 2 * (n - 1)];
661  eq_regions(dim - 1, n_in_collar, regs_1);
662  //
663  // Given regions_1, determine the dim-regions for the collar.
664  // Each element of regions_1 is a (dim-1)-region pair for the (dim-1)-sphere.
665  //
666  if (dim == 2) {
667  //
668  // The (dim-1)-sphere is a circle
669  // Offset each sector angle by an amount which accumulates over
670  // each collar.
671  //
672  for (region_1_n = 1; region_1_n <= n_in_collar; region_1_n++) {
673  //
674  // Top of 2-region
675  // The first angle is the longitude of the top of
676  // the current sector of regions_1, and
677  // the second angle is the top colatitude of the collar
678  //
679  // r_top = [mod(regions_1(1,1,region_1_n)+two_pi*offset,two_pi); c_top]
680  //
681 
682  r_top[0] = mfmod(regions_1(1, 1, region_1_n) + piconst::two_pi * offset, piconst::two_pi);
683  r_top[1] = c_top;
684  //
685  // Bottom of 2-region
686  // The first angle is the longitude of the bottom of
687  // the current sector of regions_1, and
688  // the second angle is the bottom colatitude of the collar
689  //
690  // r_bot = [mod(regions_1(1,2,region_1_n)+two_pi*offset,two_pi); c_bot]
691  //
692 
693  r_bot[0] = mfmod(regions_1(1, 2, region_1_n) + piconst::two_pi * offset, piconst::two_pi);
694  r_bot[1] = c_bot;
695  if (r_bot[0] <= r_top[0])
696  r_bot[0] += piconst::two_pi;
697 
698  region_n++;
699  /* regions(:,:,region_n) = [r_top,r_bot] */
700  regions(1, 1, region_n) = r_top[0];
701  regions(1, 2, region_n) = r_bot[0];
702  regions(2, 1, region_n) = r_top[1];
703  regions(2, 2, region_n) = r_bot[1];
704  }
705  //
706  // Given the number of sectors in the current collar and
707  // in the next collar, calculate the next offset.
708  // Accumulate the offset, and force it to be a number between 0 and 1.
709  //
710 
711  offset += circle_offset(n_in_collar, n_regions[1 + collar_n]);
712  offset -= floor(offset);
713  }
714  else {
715  for (region_1_n = 1; region_1_n <= size_regions_1_3; region_1_n++) {
716  region_n++;
717 
718  //
719  // Dim-region;
720  // The first angles are those of the current (dim-1) region of regions_1.
721  //
722 
723  for (j = 1; j <= 2; j++)
724  for (i = 1; i <= dim - 1; i++)
725  regions(i, j, region_n) = regions_1(i, j, region_1_n);
726 
727  //
728  // The last angles are the top and bottom colatitudes of the collar.
729  //
730  // regions(dim,:,region_n) = [c_top,c_bot]
731  //
732 
733  regions(dim, 1, region_n) = c_top;
734  regions(dim, 2, region_n) = c_bot;
735  }
736  }
737 
738  delete[] regs_1;
739  }
740  //
741  // End with the bottom cap.
742  //
743  // regions(:,:,N) = bot_cap_region(dim,s_cap(1))
744  //
745  bot_cap_region(dim, s_cap[0], &regions(1, 1, n));
746  }
747  }
748  delete[] s_cap;
749  delete[] n_regions;
750 }
751 
752 
753 double EqRegionCache::eq_area(const double& rn)
754 
755 {
756  int n = rn;
757  double eq_area = (sphere_area / n);
758  return eq_area;
759 }
760 
761 
762 double EqRegionCache::eq_n(const double& resol)
763 
764 {
765  double dres = (resol < min_resol ? min_resol : resol) * piconst::pi_over_180;
766  double eq_area = dres * dres; /* Actually: (resol*pi/180)^2 * R^2 (approx.) */
767  int n = NINT(sphere_area / eq_area); /* Note: R^2's would have been divided away */
768  return n;
769 }
770 
771 
772 double EqRegionCache::get_resol(const double& nval)
773 
774 {
775  return eq_n(nval);
776 }
777 
778 double EqRegionCache::eq_resol(const double& rn)
779 
780 {
781  double eq_area_value = eq_area(rn);
782  double resol = sqrt(eq_area_value) * piconst::recip_pi_over_180; /* In degrees ~ at Equator for small resol's */
783  if (resol < min_resol)
784  resol = min_resol;
785  return resol;
786 }
787 
788 
789 void EqRegionCache::create_cache(const double& resol, const int& n)
790 
791 {
792  // nothing done for this resolution
793  double* regs = NULL;
794  int jb, nb = 0;
795  int nbelm = dim * 2 * resol;
796 
797  regs = new double[nbelm];
798  eq_regions(dim, resol, &regions(1, 1, 1));
799 
800  {
801  // After eq_regions() function call, latitudes & longitudes are in radians
802  // and between [ 0 .. pi ] and [ 0 .. two_pi ], respectively.
803  //
804  // Pay special attention to cap regions (j=1 & j=n), where starting and
805  // ending longitude has the same constant value; make them -180 and +180, respectively .
806 
807  int j;
808  for (j = 1; j <= n; j++) {
809  // In radians
810  double startlon = regions(1, 1, j);
811  double startlat = regions(2, 1, j);
812  double endlon = regions(1, 2, j);
813  double endlat = regions(2, 2, j);
814  // shift latitudes to start from -half_pi, not 0
815  startlat -= piconst::half_pi;
816  endlat -= piconst::half_pi;
817 
818  if (j == 1 || j == n) { // cap regions
819  endlon = startlon;
820  }
821 
822  // Replace values in regions, still in radians
823  regions(1, 1, j) = startlon;
824  regions(2, 1, j) = startlat;
825  regions(1, 2, j) = endlon;
826  regions(2, 2, j) = endlat;
827  }
828  }
829 
830  //
831  // Derive how many (nb) latitude bands we have i.e. keep track how often
832  // the consecutive (startlat,endlat)-pairs change their value.
833  //
834 
835  int j = 1;
836  int ncnt;
837  double three_sixty = 360;
838  double last_startlat = regions(2, 1, j);
839  double last_endlat = regions(2, 2, j);
840 
841  nb = 1; /* Southern polar cap */
842  for (j = 2; j < n; j++) { // All but caps
843  double startlat = regions(2, 1, j);
844  double endlat = regions(2, 2, j);
845  if (last_startlat == startlat && last_endlat == endlat) {
846  // No change i.e. we are still in the same latitude band
847  continue;
848  }
849  else {
850  // New latitude band //
851  ++nb;
852  last_startlat = startlat;
853  last_endlat = endlat;
854  }
855  } // for (j=2; j<n; j++)
856  if (n > 1) { // Northern polar cap
857  ++nb;
858  }
859 
860  //
861  // We have now in total "nb" latitude bands.
862  // Lets store their starting and ending latitudes for much faster lookup.
863  //
864 
865  double* latband = new double[nb + 1];
866  int* loncnt = new int[nb];
867  double* stlon = new double[nb];
868  double* deltalon = new double[nb];
869 
870  jb = -1;
871 
872  j = 1;
873  last_startlat = regions(2, 1, j);
874  last_endlat = regions(2, 2, j);
875  loncnt[++jb] = 1;
876  latband[jb] = -R2D(last_startlat); // Reverse latitudes from S->N to N->S pole
877 
878  for (j = 2; j < n; j++) { // All but caps
879  double startlat = regions(2, 1, j);
880  double endlat = regions(2, 2, j);
881  if (last_startlat == startlat && last_endlat == endlat) {
882  // Increase longitude count by one for this latitude band
883  loncnt[jb]++;
884  }
885  else {
886  // For previous latitude band ...
887  ncnt = loncnt[jb];
888  deltalon[jb] = three_sixty / ncnt;
889  stlon[jb] = -deltalon[jb] / 2; // Starting longitude at 0 GMT minus half longitudinal delta
890  // New latitude band
891  last_startlat = startlat;
892  last_endlat = endlat;
893  loncnt[++jb] = 1;
894  latband[jb] = -R2D(last_startlat); // Reverse latitudes from S->N to N->S pole
895  }
896  } // for (j=2; j<n; j++)
897 
898  if (n > 1) { // Northern polar cap (mapped to be the South pole, in fact)
899  ncnt = loncnt[jb];
900  deltalon[jb] = three_sixty / ncnt;
901  stlon[jb] = -deltalon[jb] / 2; // Starting longitude at 0 GMT minus half longitudinal delta
902  j = n;
903  last_startlat = regions(2, 1, j);
904  last_endlat = regions(2, 2, j);
905  loncnt[++jb] = 1;
906  latband[jb] = -R2D(last_startlat); // Reverse latitudes from S->N to N->S pole
907  }
908 
909  {
910  ncnt = loncnt[jb];
911  deltalon[jb] = three_sixty / ncnt;
912  stlon[jb] = -deltalon[jb] / 2; // Starting longitude at 0 GMT minus half longitudinal delta
913  j = n + 1;
914  latband[++jb] = -R2D(last_endlat); // Reverse latitudes from S->N to N->S pole
915  }
916 
917  // Release space allocated by regs
918 
919  delete[] regs;
920 
921  // Calculate mid latitudes for each band by a simple averaging process
922 
923  double* midlat = new double[nb];
924  for (jb = 0; jb < nb; jb++) {
925  midlat[jb] = (latband[jb] + latband[jb + 1]) / 2;
926  }
927  // Store in cache
929  RegionCache::put_cache(kind, resol, nb, latband, midlat, stlon, deltalon, loncnt);
930 }
931 
932 } // namespace function
933 } // namespace sql
934 } // namespace odc
#define regions(i, j, k)
#define region(i, j)
#define regions_1(i, j, k)
#define NINT(x)
Definition: RegionCache.h:30
#define R2D(x)
Definition: RegionCache.h:35
double eq_area(const double &)
double eq_resol(const double &)
void bot_cap_region(int &, double &, double[])
virtual double get_resol(const double &val)
double polar_colat(int &, int &)
void ideal_region_list(int &, int &, double &, int &, double[])
void sphere_region(int &, double[])
int num_collars(int &, double &, double)
void top_cap_region(int &, double &, double[])
void cap_colats(int &, int &, int &, double &, const int[], double[])
double area_of_ideal_region(int &, int &)
double sradius_of_cap(int &, double &)
void eq_caps(int &, int &, double[], int[], int *)
void eq_regions(int, int, double[])
void round_to_naturals(int &, int &, const double[], int[])
double area_of_collar(int &, double, double)
virtual void create_cache(const double &, const int &)
double area_of_cap(int &, double &)
double ideal_collar_angle(int &, int &)
double circle_offset(int &, int &)
void put_cache(const RegionCacheKind &kind, const double &, const int &, double[], double[], double[], double[], int[])
Definition: RegionCache.cc:231
constexpr double pi_over_180
Definition: piconst.h:29
constexpr double recip_pi_over_180
Definition: piconst.h:30
constexpr double pi
Definition: piconst.h:25
constexpr double four_pi
Definition: piconst.h:28
constexpr double half_pi
Definition: piconst.h:26
constexpr double two_pi
Definition: piconst.h:27
static const int dim
Definition: RegionCache.h:26
double mfmod(double x, double y)
Definition: RegionCache.cc:22
static const double min_resol
Definition: RegionCache.h:27
static const double sphere_area
Definition: RegionCache.h:28
Definition: ColumnInfo.h:23