-
Notifications
You must be signed in to change notification settings - Fork 375
/
Copy pathsurface.c
2296 lines (2047 loc) · 120 KB
/
surface.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*--------------------------------------------------------------------
*
* Copyright (c) 1991-2025 by the GMT Team (https://www.generic-mapping-tools.org/team.html)
* See LICENSE.TXT file for copying and redistribution conditions.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 3 or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* Contact info: www.generic-mapping-tools.org
*--------------------------------------------------------------------*/
/*
* surface.c: a gridding program using splines in tension.
* Reads xyz Cartesian triples and fits a surface to the data.
* The surface satisfies (1 - T) D4 z - T D2 z = 0,
* where D4 is the 2-D biharmonic operator, D2 is the
* 2-D Laplacian, and T is a "tension factor" between 0 and 1.
* End member T = 0 is the classical minimum curvature
* surface. T = 1 gives a harmonic surface. Use T = 0.25
* or so for potential data; something more for topography.
*
* Program includes over-relaxation for fast convergence and
* automatic optimal grid factorization.
*
* See reference Smith & Wessel (Geophysics, 3, 293-305, 1990) for details.
*
* Authors: Walter H. F. Smith and Paul Wessel
* Date: 1-JAN-2010
* Version: 6 API
*
* This 6.0 version is a complete re-write that differs from the original code:
* 1. It uses scan-line grid structures, so we no longer need to transpose grids
* 2. It keeps node spacing at 1, thus we no longer need complicated strides between
* active nodes. That spacing is now always 1 and we expand the grid as we
* go to larger grids (i.e., adding more nodes).
* 3. It relies more on functions and macros from GMT to handle rows/cols/node calculations.
*
* Note on KEYS: DD(= means -D takes an optional input Dataset as argument which may be followed by optional modifiers.
*
* Update for 6.4.0: We help users get a better interpolation by selecting the most optimal -R to
* result in many intermediate grid spacings for the multigrid progression to provide the best
* convergence, then shrink back to the requested region upon output. Experts who wishes to defeat this
* improvement can use -Qr which will honor the given -R exactly, even if prime.
*/
#include "gmt_dev.h"
#include "longopt/surface_inc.h"
#define THIS_MODULE_CLASSIC_NAME "surface"
#define THIS_MODULE_MODERN_NAME "surface"
#define THIS_MODULE_LIB "core"
#define THIS_MODULE_PURPOSE "Grid table data using adjustable tension continuous curvature splines"
#define THIS_MODULE_KEYS "<D{,DD(=,LG(,GG}"
#define THIS_MODULE_NEEDS "R"
#define THIS_MODULE_OPTIONS "-:RVabdefhiqrw" GMT_OPT("FH")
struct SURFACE_CTRL {
struct SURFACE_A { /* -A<aspect_ratio> */
bool active;
unsigned int mode; /* 1 if given as fraction */
double value;
} A;
struct SURFACE_C { /* -C<converge_limit> */
bool active;
unsigned int mode; /* 1 if given as fraction */
double value;
} C;
struct SURFACE_D { /* -D<line.xyz>[+d][+z[<zval>]] */
bool active;
bool debug;
bool fix_z;
double z;
char *file; /* Name of file with breaklines */
} D;
struct SURFACE_G { /* -G<file> */
bool active;
char *file;
} G;
struct SURFACE_I { /* -I (for checking only) */
bool active;
} I;
struct SURFACE_J { /* -G<file> */
bool active;
char *projstring;
} J;
struct SURFACE_L { /* -Ll|u<limit> */
bool active[2];
char *file[2];
double limit[2];
unsigned int mode[2];
} L;
struct SURFACE_M { /* -M<radius> */
bool active;
char *arg;
} M;
struct SURFACE_N { /* -N<max_iterations> */
bool active;
unsigned int value;
} N;
struct SURFACE_Q { /* -Q[r] */
bool active; /* Information sought */
bool as_is; /* Use -R exactly as given */
bool adjusted; /* Improved -R required pad changes */
double wesn[4]; /* Best improved -R for this operation */
} Q;
struct SURFACE_S { /* -S<radius>[m|s] */
bool active;
double radius;
char unit;
} S;
struct SURFACE_T { /* -T<tension>[i][b] */
bool active[2];
double b_tension, i_tension;
} T;
struct SURFACE_W { /* -W[<logfile>] */
bool active;
char *file;
} W;
struct SURFACE_Z { /* -Z<over_relaxation> */
bool active;
double value;
} Z;
};
/* Various constants used in surface */
#define SURFACE_OUTSIDE LONG_MAX /* Index number indicating data is outside usable area */
#define SURFACE_CONV_LIMIT 0.0001 /* Default is 100 ppm of data range as convergence criterion */
#define SURFACE_MAX_ITERATIONS 500 /* Default iterations at final grid size */
#define SURFACE_OVERRELAXATION 1.4 /* Default over-relaxation value */
#define SURFACE_CLOSENESS_FACTOR 0.05 /* A node is considered known if the nearest data is within 0.05 of a gridspacing of the node */
#define SURFACE_IS_UNCONSTRAINED 0 /* Node has no data constraint within its bin box */
#define SURFACE_DATA_IS_IN_QUAD1 1 /* Nearnest data constraint is in quadrant 1 relative to current node */
#define SURFACE_DATA_IS_IN_QUAD2 2 /* Nearnest data constraint is in quadrant 2 relative to current node */
#define SURFACE_DATA_IS_IN_QUAD3 3 /* Nearnest data constraint is in quadrant 3 relative to current node */
#define SURFACE_DATA_IS_IN_QUAD4 4 /* Nearnest data constraint is in quadrant 4 relative to current node */
#define SURFACE_IS_CONSTRAINED 5 /* Node has already been set (either data constraint < 5% of grid size or during filling) */
#define SURFACE_UNCONSTRAINED 0 /* Use coefficients set for unconstrained node */
#define SURFACE_CONSTRAINED 1 /* Use coefficients set for constrained node */
#define SURFACE_BREAKLINE 1 /* Flag for breakline constraints that should overrule data constraints */
/* Misc. macros used to get row, cols, index, node, x, y, plane trend etc. */
/* Go from row, col to grid node location, accounting for the 2 boundary rows and columns: */
#define row_col_to_node(row,col,mx) ((uint64_t)(((int64_t)(row)+(int64_t)2)*((int64_t)(mx))+(int64_t)(col)+(int64_t)2))
/* Go from row, col to index array position, where no boundary rows and columns involved: */
#define row_col_to_index(row,col,n_columns) ((uint64_t)((int64_t)(row)*((int64_t)(n_columns))+(int64_t)(col)))
/* Go from data x to fractional column x: */
#define x_to_fcol(x,x0,idx) (((x) - (x0)) * (idx))
/* Go from x to grid integer column knowing it is a gridline-registered grid: */
#define x_to_col(x,x0,idx) ((int64_t)floor(x_to_fcol(x,x0,idx)+0.5))
/* Go from data y to fractional row y_up measured from south (y_up = 0) towards north (y_up = n_rows-1): */
#define y_to_frow(y,y0,idy) (((y) - (y0)) * (idy))
/* Go from y to row (row = 0 is north) knowing it is a gridline-registered grid: */
#define y_to_row(y,y0,idy,n_rows) ((n_rows) - 1 - x_to_col(y,y0,idy))
/* Go from col to x knowing it is a gridline-registered grid: */
#define col_to_x(col,x0,x1,dx,n_columns) (((int)(col) == (int)((n_columns)-1)) ? (x1) : (x0) + (col) * (dx))
/* Go from row to y knowing it is a gridline-registered grid: */
#define row_to_y(row,y0,y1,dy,n_rows) (((int)(row) == (int)((n_rows)-1)) ? (y0) : (y1) - (row) * (dy))
/* Evaluate the change in LS plane from (0,0) to (xx,y_up) (so in intercept involved): */
#define evaluate_trend(C,xx,y_up) (C->plane_sx * (xx) + C->plane_sy * (y_up))
/* Evaluate the LS plane at location (xx,y_up) (this includes the intercept): */
#define evaluate_plane(C,xx,y_up) (C->plane_icept + evaluate_trend (C, xx, y_up))
/* Extract col from index: */
#define index_to_col(index,n_columns) ((index) % (n_columns))
/* Extract row from index: */
#define index_to_row(index,n_columns) ((index) / (n_columns))
enum surface_nodes { /* Node locations relative to current node, using compass directions */
N2 = 0, NW, N1, NE, W2, W1, E1, E2, SW, S1, SE, S2 }; /* I.e., 0-11 */
/* The 4 indices per quadrant refer to points A-D in Figure A-1 in the reference for quadrant 1 */
static unsigned int p[5][4] = { /* Indices into C->offset for each of the 4 quadrants, i.e., C->offset[p[quadrant][k]]], k = 0-3 */
{ 0, 0, 0, 0}, /* This row is never used, so allows us to use quadrant 1-4 as first array index directly */
{ NW, W1, S1, SE}, /* Indices for 1st quadrant */
{ SW, S1, E1, NE}, /* Indices for 2nd quadrant */
{ SE, E1, N1, NW}, /* Indices for 3rd quadrant */
{ NE, N1, W1, SW} /* Indices for 4th quadrant */
};
enum surface_bound { LO = 0, HI = 1 };
enum surface_limit { NONE = 0, DATA = 1, VALUE = 2, SURFACE = 3 };
enum surface_conv { BY_VALUE = 0, BY_PERCENT = 1 };
enum surface_iter { GRID_NODES = 0, GRID_DATA = 1 };
enum surface_tension { BOUNDARY = 0, INTERIOR = 1 };
struct SURFACE_DATA { /* Data point and index to node it currently constrains */
gmt_grdfloat x, y, z;
unsigned int kind;
uint64_t index;
};
struct SURFACE_BRIGGS { /* Coefficients in Taylor series for Laplacian(z) a la I. C. Briggs (1974) */
gmt_grdfloat b[6];
};
struct SURFACE_SEARCH { /* Things needed inside compare function will be passed to QSORT_R */
int current_nx; /* Number of nodes in y-dir for a given grid factor */
int current_ny; /* Number of nodes in y-dir for a given grid factor */
double inc[2]; /* Size of each grid cell for a given grid factor */
double wesn[4]; /* Grid domain */
};
struct SURFACE_INFO { /* Control structure for surface setup and execution */
size_t n_alloc; /* Number of data point positions allocated */
uint64_t npoints; /* Number of data points */
uint64_t node_sw_corner; /* Node index of southwest interior grid corner for current stride */
uint64_t node_se_corner; /* Node index of southeast interior grid corner for current stride */
uint64_t node_nw_corner; /* Node index of northwest interior grid corner for current stride */
uint64_t node_ne_corner; /* Node index of northeast interior grid corner for current stride */
uint64_t n_empty; /* No of unconstrained nodes at initialization */
uint64_t nxny; /* Total number of grid nodes without boundaries */
uint64_t mxmy; /* Total number of grid nodes with padding */
uint64_t total_iterations; /* Total iterations so far. */
FILE *fp_log; /* File pointer to log file, if -W is selected */
struct SURFACE_DATA *data; /* All the data constraints */
struct SURFACE_BRIGGS *Briggs; /* Array with Briggs 6-coefficients per nearest active data constraint */
struct GMT_GRID *Grid; /* The final grid */
struct GMT_GRID *Bound[2]; /* Optional grids for lower and upper limits on the solution */
struct GMT_GRID_HEADER *Bh; /* Grid header for one of the limit grids [or NULL] */
struct SURFACE_SEARCH info; /* Information needed by the compare function passed to QSORT_R */
unsigned int n_factors; /* Number of factors in common for the dimensions (n_rows-1, n_columns-1) */
unsigned int factors[32]; /* Array of these common factors */
unsigned int set_limit[2]; /* For low and high: NONE = unconstrained, DATA = by min data value, VALUE = by user value, SURFACE by a grid */
unsigned int max_iterations; /* Max iterations per call to iterate */
unsigned int converge_mode; /* BY_PERCENT if -C set fractional convergence limit [BY_VALUE] */
unsigned int p[5][4]; /* Arrays with four nodes as function of quadrant in constrained fit */
unsigned int q_pad[4]; /* Extra padding needed for constrain grids if wesn is extended */
int current_stride; /* Current node spacings relative to final spacing */
int previous_stride; /* Previous node spacings relative to final spacing */
int n_columns; /* Number of nodes in x-dir. (Final grid) */
int n_rows; /* Number of nodes in y-dir. (Final grid) */
int mx; /* Width of final grid including padding */
int my; /* Height of final grid including padding */
int current_nx; /* Number of nodes in x-dir for current stride */
int current_ny; /* Number of nodes in y-dir for current stride */
int current_mx; /* Number of current nodes in x-dir plus 4 extra columns */
int previous_nx; /* Number of nodes in x-dir for previous stride */
int previous_ny; /* Number of nodes in y-dir for previous stride */
int previous_mx; /* Number of current nodes in x-dir plus 4 extra columns */
int current_mxmy; /* Total number of grid nodes with padding */
int offset[12]; /* Node-indices shifts of 12 nearby points relative center node */
unsigned char *status; /* Array with node status or quadrants */
char mode_type[2]; /* D = include data points when iterating, I = just interpolate from larger grid */
char format[GMT_BUFSIZ]; /* Format statement used in some messages */
char *limit_file[2]; /* Pointers to grids with low and high limits, if selected */
bool periodic; /* true if geographic grid and west-east == 360 */
bool constrained; /* true if set_limit[LO] or set_limit[HI] is true */
bool logging; /* true if -W was specified */
bool adjusted; /* true if -L grids need to be enlarged with pads */
double limit[2]; /* Low and high constrains on range of solution */
double inc[2]; /* Size of each grid cell for current grid factor */
double r_inc[2]; /* Reciprocal grid spacings */
double converge_limit; /* Convergence limit */
double radius; /* Search radius for initializing grid */
double tension; /* Tension parameter on the surface */
double boundary_tension; /* Tension parameter at the boundary */
double interior_tension; /* Tension parameter in the interior */
double z_mean; /* Mean value of the data constraints z */
double z_rms; /* Root mean square range of z after removing planar trend */
double r_z_rms; /* Reciprocal of z_rms (to avoid dividing) */
double plane_icept; /* Intercept of best fitting plane to data */
double plane_sx; /* Slope of best fitting plane to data in x-direction */
double plane_sy; /* Slope of best fitting plane to data in y-direction */
double *fraction; /* Hold fractional increments of row and column used in fill_in_forecast */
double coeff[2][12]; /* Coefficients for 12 nearby nodes, for constrained [0] and unconstrained [1] nodes */
double relax_old, relax_new; /* Coefficients for relaxation factor to speed up convergence */
double wesn_orig[4]; /* Original -R domain as we might have shifted it due to -r */
double alpha; /* Aspect ratio dy/dx (1 for square pixels) */
double a0_const_1, a0_const_2; /* Various constants for off gridnode point equations */
double alpha2, e_m2, one_plus_e2;
double eps_p2, eps_m2, two_plus_ep2;
double two_plus_em2;
};
GMT_LOCAL void surface_set_coefficients (struct GMT_CTRL *GMT, struct SURFACE_INFO *C) {
/* These are the coefficients in the finite-difference expressions given
* by equations (A-4) [SURFACE_UNCONSTRAINED=0] and (A-7) [SURFACE_CONSTRAINED=1] in the reference.
* Note that the SURFACE_UNCONSTRAINED coefficients are normalized by a0 (20 for no tension/aspects)
* whereas the SURFACE_CONSTRAINED is used for a partial sum hence the normalization is done when the
* sum over the Briggs coefficients have been included in iterate. */
double alpha4, loose, a0;
GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Set finite-difference coefficients [stride = %d]\n", C->current_stride);
loose = 1.0 - C->interior_tension;
C->alpha2 = C->alpha * C->alpha;
alpha4 = C->alpha2 * C->alpha2;
C->eps_p2 = C->alpha2;
C->eps_m2 = 1.0 / C->alpha2;
C->one_plus_e2 = 1.0 + C->alpha2;
C->two_plus_ep2 = 2.0 + 2.0 * C->eps_p2;
C->two_plus_em2 = 2.0 + 2.0 * C->eps_m2;
C->e_m2 = 1.0 / C->alpha2;
a0 = 1.0 / ( (6 * alpha4 * loose + 10 * C->alpha2 * loose + 8 * loose - 2 * C->one_plus_e2) + 4 * C->interior_tension * C->one_plus_e2);
C->a0_const_1 = 2.0 * loose * (1.0 + alpha4);
C->a0_const_2 = 2.0 - C->interior_tension + 2 * loose * C->alpha2;
C->coeff[SURFACE_CONSTRAINED][W2] = C->coeff[SURFACE_CONSTRAINED][E2] = -loose;
C->coeff[SURFACE_CONSTRAINED][N2] = C->coeff[SURFACE_CONSTRAINED][S2] = -loose * alpha4;
C->coeff[SURFACE_UNCONSTRAINED][W2] = C->coeff[SURFACE_UNCONSTRAINED][E2] = -loose * a0;
C->coeff[SURFACE_UNCONSTRAINED][N2] = C->coeff[SURFACE_UNCONSTRAINED][S2] = -loose * alpha4 * a0;
C->coeff[SURFACE_CONSTRAINED][W1] = C->coeff[SURFACE_CONSTRAINED][E1] = 2 * loose * C->one_plus_e2;
C->coeff[SURFACE_UNCONSTRAINED][W1] = C->coeff[SURFACE_UNCONSTRAINED][E1] = (2 * C->coeff[SURFACE_CONSTRAINED][W1] + C->interior_tension) * a0;
C->coeff[SURFACE_CONSTRAINED][N1] = C->coeff[SURFACE_CONSTRAINED][S1] = C->coeff[SURFACE_CONSTRAINED][W1] * C->alpha2;
C->coeff[SURFACE_UNCONSTRAINED][N1] = C->coeff[SURFACE_UNCONSTRAINED][S1] = C->coeff[SURFACE_UNCONSTRAINED][W1] * C->alpha2;
C->coeff[SURFACE_CONSTRAINED][NW] = C->coeff[SURFACE_CONSTRAINED][NE] = C->coeff[SURFACE_CONSTRAINED][SW] =
C->coeff[SURFACE_CONSTRAINED][SE] = -2 * loose * C->alpha2;
C->coeff[SURFACE_UNCONSTRAINED][NW] = C->coeff[SURFACE_UNCONSTRAINED][NE] = C->coeff[SURFACE_UNCONSTRAINED][SW] =
C->coeff[SURFACE_UNCONSTRAINED][SE] = C->coeff[SURFACE_CONSTRAINED][NW] * a0;
C->alpha2 *= 2; /* We will need these coefficients times two in the boundary conditions; do the doubling here */
C->e_m2 *= 2;
}
GMT_LOCAL void surface_set_offset (struct SURFACE_INFO *C) {
/* The offset array holds the offset in 1-D index relative
* to the current node. For movement along a row this is
* always -2, -1, 0, +1, +2 but along a column we move in
* multiples of current_mx, the extended grid row width,
* which is current_mx = current_nx + 4.
*/
C->offset[N2] = -2 * C->current_mx; /* N2: 2 rows above */
C->offset[NW] = -C->current_mx - 1; /* NW: 1 row above and one column left */
C->offset[N1] = -C->current_mx; /* N1: 1 row above */
C->offset[NE] = -C->current_mx + 1; /* NE: 1 row above and one column right */
C->offset[W2] = -2; /* W2: 2 columns left */
C->offset[W1] = -1; /* W1 : 1 column left */
C->offset[E1] = +1; /* E1 : 1 column right */
C->offset[E2] = +2; /* E2 : 2 columns right */
C->offset[SW] = C->current_mx - 1; /* SW : 1 row below and one column left */
C->offset[S1] = C->current_mx; /* S1 : 1 row below */
C->offset[SE] = C->current_mx + 1; /* SE : 1 row below and one column right */
C->offset[S2] = 2 * C->current_mx; /* S2 : 2 rows below */
}
GMT_LOCAL void fill_in_forecast (struct GMT_CTRL *GMT, struct SURFACE_INFO *C) {
/* Fills in bilinear estimates into new node locations after grid is expanded.
These new nodes are marked as unconstrained while the coarser data are considered
constraints in the next iteration. We do this in two steps:
a) We sweep through the grid from last to first node and copy each node to the
new location due to increased grid dimensions.
b) Once nodes are in place we sweep through and apply the bilinear interpolation.
*/
uint64_t index_00, index_10, index_11, index_01, index_new, current_node, previous_node;
int previous_row, previous_col, i, j, col, row, expand, first;
unsigned char *status = C->status;
double c, sx, sy, sxy, r_prev_size, c_plus_sy_dy, sx_plus_sxy_dy;
gmt_grdfloat *u = C->Grid->data;
/* First we expand the active grid to allow for more nodes. We do this by
* looping backwards from last node to first so that the locations we copy
* the old node values to will always have higher node number than their source.
* The previous grid solution has dimensions previous_nx x previous_ny while
* the new grid has dimensions current_nx x current_ny. We thus loop over
* the old grid and place these nodes into the new grid. */
expand = C->previous_stride / C->current_stride; /* Multiplicity of new nodes in both x and y dimensions */
GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Expand grid by factor of %d when going from stride = %d to %d\n", expand, C->previous_stride, C->current_stride);
for (previous_row = C->previous_ny - 1; previous_row >= 0; previous_row--) { /* Loop backward over the previous grid rows */
row = previous_row * expand; /* Corresponding row in the new extended grid */
for (previous_col = C->previous_nx - 1; previous_col >= 0; previous_col--) { /* Loop backward over previous grid cols */
col = previous_col * expand; /* Corresponding col in the new extended grid */
current_node = row_col_to_node (row, col, C->current_mx); /* Current node index */
previous_node = row_col_to_node (previous_row, previous_col, C->previous_mx); /* Previous node index */
C->Grid->data[current_node] = C->Grid->data[previous_node]; /* Copy the value over */
}
}
/* The active grid has now increased in size and the previous values have been copied to their new nodes.
* The grid nodes in-between these new "constrained" nodes are partly filled with old values (since
* we just copied, not moved, the nodes) or zeros (since we expanded the grid into new unused memory).
* This does not matter since we will now fill in those in-between nodes with a bilinear interpolation
* based on the coarser (previous) nodes. At the end all nodes in the active grid are valid, except
* in the boundary rows/cols. These are reset by set_BC before the iteration starts.
*/
/* Precalculate the fractional increments of rows and cols in-between the old constrained rows and cols.
* These are all fractions between 0 and 1. E.g., if we quadruple the grid dimensions in x and y then
* expand == 4 and we need 4 fractions = {0, 0.25, 0.5, 0.75}. */
r_prev_size = 1.0 / (double)C->previous_stride;
for (i = 0; i < expand; i++) C->fraction[i] = i * r_prev_size;
GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Fill in expanded grid by bilinear interpolation [stride = %d]\n", C->current_stride);
/* Loop over 4-point "bin squares" from the first northwest bin to the last southeast bin. The bin vertices are the expanded previous nodes */
for (previous_row = 1; previous_row < C->previous_ny; previous_row++) { /* Starts at row 1 since it is the baseline for the bin extending up to row 0 (north) */
row = previous_row * expand; /* Corresponding row in the new extended grid */
for (previous_col = 0; previous_col < (C->previous_nx-1); previous_col++) { /* Stop 1 column short of east since east is the right boundary of last bin */
col = previous_col * expand; /* Corresponding col in the new extended grid */
/* Get the indices of the bilinear square defined by nodes {00, 10, 11, 01}, with 00 referring to the current (lower left) node */
index_00 = row_col_to_node (row, col, C->current_mx); /* Lower left corner of square bin and our origin */
index_01 = index_00 - expand * C->current_mx; /* Upper left corner of square bin */
index_10 = index_00 + expand; /* Lower right corner of square bin */
index_11 = index_01 + expand; /* Upper right corner of square bin */
/* Get bilinear coefficients for interpolation z = c + sx * delta_x + sy * delta_y + sxy * delta_x * delta_y,
* which we will use as z = (c + sy * delta_y) + delta_x * (sx + sxy * delta_y).
* Below, delta_x and delta_y are obtained via C->fraction[i|j] that we pre-calculated above. */
c = u[index_00]; sx = u[index_10] - c;
sy = u[index_01] - c; sxy = u[index_11] - u[index_10] - sy;
/* Fill in all the denser nodes except the lower-left starting point */
for (j = 0, first = 1; j < expand; j++) { /* Set first = 1 so we skip the first column when j = 0 */
c_plus_sy_dy = c + sy * C->fraction[j]; /* Compute terms that remain constant for this j */
sx_plus_sxy_dy = sx + sxy * C->fraction[j];
index_new = index_00 - j * C->current_mx + first; /* Start node on this intermediate row */
for (i = first; i < expand; i++, index_new++) { /* Sweep across this row and interpolate */
u[index_new] = (gmt_grdfloat)(c_plus_sy_dy + C->fraction[i] * sx_plus_sxy_dy);
status[index_new] = SURFACE_IS_UNCONSTRAINED; /* These are considered temporary estimates */
}
first = 0; /* Reset to 0 for the remainder of the j loop */
}
status[index_00] = SURFACE_IS_CONSTRAINED; /* The previous node values will be kept fixed in the next iterate call */
}
}
/* The loops above exclude the north and east boundaries. First do linear interpolation along the east edge */
index_00 = C->node_ne_corner; /* Upper NE node */
for (previous_row = 1; previous_row < C->previous_ny; previous_row++) { /* So first edge is from row = 1 up to row = 0 on eastern edge */
index_01 = index_00; /* Previous lower becomes current upper node */
index_00 += expand * C->current_mx; /* Lower node after striding down */
sy = u[index_01] - u[index_00]; /* Vertical gradient in u toward ymax (for increasing j) */
index_new = index_00 - C->current_mx; /* Since we start at j = 1 we skip up one row here */
for (j = 1; j < expand; j++, index_new -= C->current_mx) { /* Start at 1 since we skip the constrained index_00 node */
u[index_new] = u[index_00] + (gmt_grdfloat)(C->fraction[j] * sy);
status[index_new] = SURFACE_IS_UNCONSTRAINED; /* These are considered temporary estimates */
}
status[index_00] = SURFACE_IS_CONSTRAINED; /* The previous node values will be kept fixed in the next iterate call */
}
/* Next do linear interpolation along the north edge */
index_10 = C->node_nw_corner; /* Left NW node */
for (previous_col = 0; previous_col < (C->previous_nx-1); previous_col++) { /* To ensure last edge ends at col = C->previous_nx-1 */
index_00 = index_10; /* Previous right node becomes current left node */
index_10 = index_00 + expand; /* Right node after striding to the right */
sx = u[index_10] - u[index_00]; /* Horizontal gradient in u toward xmax (for increasing i) */
index_new = index_00 + 1; /* Start at 1 since we skip the constrained index_00 node */
for (i = 1; i < expand; i++, index_new++) {
u[index_new] = u[index_00] + (gmt_grdfloat)(C->fraction[i] * sx);
status[index_new] = SURFACE_IS_UNCONSTRAINED; /* These are considered temporary estimates */
}
status[index_00] = SURFACE_IS_CONSTRAINED; /* The previous node values will be kept fixed in the next iterate call */
}
/* Finally set the northeast corner to be considered fixed in the next iterate call and our work here is done */
status[C->node_ne_corner] = SURFACE_IS_CONSTRAINED;
}
#ifdef QSORT_R_THUNK_FIRST
/* thunk arg is first argument to compare function */
GMT_LOCAL int surface_compare_points (void *arg, const void *point_1v, const void *point_2v) {
#else
/* thunk arg is last argument to compare function */
GMT_LOCAL int surface_compare_points (const void *point_1v, const void *point_2v, void *arg) {
#endif
/* Routine for QSORT_R to sort data structure for fast access to data by node location.
Sorts on index first, then on radius to node corresponding to index, so that index
goes from low to high, and so does radius. Note: These are simple Cartesian distance
* calculations. The metadata needed to do the calculations are passed via *arg.
*/
uint64_t col, row, index_1, index_2;
double x0, y0, dist_1, dist_2;
const struct SURFACE_DATA *point_1 = point_1v, *point_2 = point_2v;
struct SURFACE_SEARCH *info;
index_1 = point_1->index;
index_2 = point_2->index;
if (index_1 < index_2) return (-1);
if (index_1 > index_2) return (+1);
if (index_1 == SURFACE_OUTSIDE) return (0);
/* Points are in same grid cell. First check for breakline points to sort those ahead of data points */
if (point_1->kind == SURFACE_BREAKLINE && point_2->kind == 0) return (-1);
if (point_2->kind == SURFACE_BREAKLINE && point_1->kind == 0) return (+1);
/* Now find the one who is nearest to grid point */
/* Note: index calculations do not include boundary pad */
info = arg; /* Get the needed metadata for distance calculations */
row = index_to_row (index_1, info->current_nx);
col = index_to_col (index_1, info->current_nx);
x0 = col_to_x (col, info->wesn[XLO], info->wesn[XHI], info->inc[GMT_X], info->current_nx);
y0 = row_to_y (row, info->wesn[YLO], info->wesn[YHI], info->inc[GMT_Y], info->current_ny);
dist_1 = (point_1->x - x0) * (point_1->x - x0) + (point_1->y - y0) * (point_1->y - y0);
/* Try to speed things up by first checking if point_2 x-distance from x0 alone exceeds point_1's radial distance */
dist_2 = (point_2->x - x0) * (point_2->x - x0); /* Just dx^2 */
if (dist_1 < dist_2) return (-1); /* Don't need to consider the y-distance */
/* Did not exceed, so now we must finalize the dist_2 calculation by including the y-separation */
dist_2 += (point_2->y - y0) * (point_2->y - y0);
if (dist_1 < dist_2) return (-1);
if (dist_1 > dist_2) return (+1);
return (0);
}
GMT_LOCAL void surface_smart_divide (struct SURFACE_INFO *C) {
/* Divide grid by its next largest prime factor and shift that setting by one */
C->current_stride /= C->factors[C->n_factors - 1];
C->n_factors--;
}
GMT_LOCAL void surface_set_index (struct GMT_CTRL *GMT, struct SURFACE_INFO *C) {
/* Recomputes data[k].index for the new value of the stride,
sorts the data again on index and radii, and throws away
data which are now outside the usable limits.
Note: These indices exclude the padding. */
int col, row;
uint64_t k, k_skipped = 0;
struct GMT_GRID_HEADER *h = C->Grid->header;
GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Recompute data index for next iteration [stride = %d]\n", C->current_stride);
for (k = 0; k < C->npoints; k++) {
col = (int)x_to_col (C->data[k].x, h->wesn[XLO], C->r_inc[GMT_X]);
row = (int)y_to_row (C->data[k].y, h->wesn[YLO], C->r_inc[GMT_Y], C->current_ny);
if (col < 0 || col >= C->current_nx || row < 0 || row >= C->current_ny) {
C->data[k].index = SURFACE_OUTSIDE;
k_skipped++;
}
else
C->data[k].index = row_col_to_index (row, col, C->current_nx);
}
QSORT_R (C->data, C->npoints, sizeof (struct SURFACE_DATA), surface_compare_points, &(C->info));
C->npoints -= k_skipped;
}
GMT_LOCAL void surface_solve_Briggs_coefficients (struct SURFACE_INFO *C, gmt_grdfloat *b, double xx, double yy, gmt_grdfloat z) {
/* Given the normalized offset (xx,yy) from current node (value z) we determine the
* Briggs coefficients b_k, k = 1,5 [Equation (A-6) in the reference]
* Here, xx, yy are the fractional distances, accounting for any anisotropy.
* Note b[5] initially contains the sum of the 5 Briggs coefficients but
* we actually need to divide by it so we do that change here as well.
* Finally, b[4] will be multiplied with the off-node constraint so we do that here.
*/
double xx2, yy2, xx_plus_yy, xx_plus_yy_plus_one, inv_xx_plus_yy_plus_one, inv_delta, b_4;
xx_plus_yy = xx + yy;
xx_plus_yy_plus_one = 1.0 + xx_plus_yy;
inv_xx_plus_yy_plus_one = 1.0 / xx_plus_yy_plus_one;
xx2 = xx * xx; yy2 = yy * yy;
inv_delta = inv_xx_plus_yy_plus_one / xx_plus_yy;
b[0] = (gmt_grdfloat)((xx2 + 2.0 * xx * yy + xx - yy2 - yy) * inv_delta);
b[1] = (gmt_grdfloat)(2.0 * (yy - xx + 1.0) * inv_xx_plus_yy_plus_one);
b[2] = (gmt_grdfloat)(2.0 * (xx - yy + 1.0) * inv_xx_plus_yy_plus_one);
b[3] = (gmt_grdfloat)((-xx2 + 2.0 * xx * yy - xx + yy2 + yy) * inv_delta);
b_4 = 4.0 * inv_delta;
/* We also need to normalize by the sum of the b[k] values, so sum them here */
b[5] = b[0] + b[1] + b[2] + b[3] + (gmt_grdfloat)b_4;
/* We need to sum k = 0<5 of u[k]*b[k], where u[k] are the nodes of the points A-D,
* but the k = 4 point (E) is our data constraint. We multiply that in here, once,
* add add b[4] to the rest of the sum inside the iteration loop. */
b[4] = (gmt_grdfloat)(b_4 * z);
/* b[5] is part of a denominator so we do the division here instead of inside iterate loop */
b[5] = (gmt_grdfloat)(1.0 / (C->a0_const_1 + C->a0_const_2 * b[5]));
}
GMT_LOCAL void surface_find_nearest_constraint (struct GMT_CTRL *GMT, struct SURFACE_INFO *C) {
/* Determines the nearest data point per bin and sets the
* Briggs parameters or, if really close, fixes the node value */
uint64_t k, last_index, node, briggs_index, node_final;
openmp_int row, col;
double xx, yy, x0, y0, dx, dy;
gmt_grdfloat z_at_node, *u = C->Grid->data;
unsigned char *status = C->status;
struct GMT_GRID_HEADER *h = C->Grid->header;
GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Determine nearest point and set Briggs coefficients [stride = %d]\n", C->current_stride);
gmt_M_grd_loop (GMT, C->Grid, row, col, node) { /* Reset status of all interior grid nodes */
status[node] = SURFACE_IS_UNCONSTRAINED;
}
last_index = UINTMAX_MAX; briggs_index = 0U;
for (k = 0; k < C->npoints; k++) { /* Find constraining value */
if (C->data[k].index != last_index) { /* Moving to the next node to address its nearest data constraint */
/* Note: Index calculations do not consider the boundary padding */
row = (openmp_int)index_to_row (C->data[k].index, C->current_nx);
col = (openmp_int)index_to_col (C->data[k].index, C->current_nx);
last_index = C->data[k].index; /* Now this is the last unique index we worked on */
node = row_col_to_node (row, col, C->current_mx);
/* Get coordinates of this node */
x0 = col_to_x (col, h->wesn[XLO], h->wesn[XHI], C->inc[GMT_X], C->current_nx);
y0 = row_to_y (row, h->wesn[YLO], h->wesn[YHI], C->inc[GMT_Y], C->current_ny);
/* Get offsets dx,dy of data point location relative to this node (dy is positive up) in fraction of grid increments */
dx = x_to_fcol (C->data[k].x, x0, C->r_inc[GMT_X]);
dy = y_to_frow (C->data[k].y, y0, C->r_inc[GMT_Y]);
/* So dx, dy are here fractions of C->inc[GMT_X] and C-inc[GMT_Y] */
/* "Really close" will mean within 5% of the current grid spacing from the center node */
if (fabs (dx) < SURFACE_CLOSENESS_FACTOR && fabs (dy) < SURFACE_CLOSENESS_FACTOR) { /* Considered close enough to assign fixed value to node */
status[node] = SURFACE_IS_CONSTRAINED;
/* Since data constraint is forcibly moved from (dx, dy) to (0,0) we must adjust for
* the small change in the planar trend between the two locations, and then
* possibly clip the value if constraining surfaces were given. Note that
* dx, dy is in -1/1 range normalized by (current_x|y_inc) so to recover the
* corresponding dx,dy in units of current grid fractions we must scale both
* dx and dy by current_stride; this is equivalent to scaling the trend.
* This trend then is normalized by dividing by the z rms.*/
z_at_node = C->data[k].z + (gmt_grdfloat) (C->r_z_rms * C->current_stride * evaluate_trend (C, dx, dy));
if (C->constrained) { /* Must use final spacing node index to access the Bound grids */
node_final = gmt_M_ijp (C->Bh, C->current_stride * row, C->current_stride * col);
if (C->set_limit[LO] && !gmt_M_is_fnan (C->Bound[LO]->data[node_final]) && z_at_node < C->Bound[LO]->data[node_final])
z_at_node = C->Bound[LO]->data[node_final];
else if (C->set_limit[HI] && !gmt_M_is_fnan (C->Bound[HI]->data[node_final]) && z_at_node > C->Bound[HI]->data[node_final])
z_at_node = C->Bound[HI]->data[node_final];
}
u[node] = z_at_node;
}
else { /* We have a nearby data point in one of the quadrants */
/* Note: We must swap dx,dy for 2nd and 4th quadrants and always use absolute values since we are
rotating other cases (quadrants 2-4) to look like quadrant 1 */
if (dy >= 0.0) { /* Upper two quadrants */
if (dx >= 0.0) { /* Both positive, use as is */
status[node] = SURFACE_DATA_IS_IN_QUAD1;
xx = dx; yy = dy;
}
else { /* dx negative, so remove sign, and swap */
status[node] = SURFACE_DATA_IS_IN_QUAD2;
yy = -dx; xx = dy;
}
}
else { /* Lower two quadrants where we need to remove sign from dy */
if (dx >= 0.0) { /* Also swap x and y */
status[node] = SURFACE_DATA_IS_IN_QUAD4;
yy = dx; xx = -dy;
}
else { /* Just remove both signs */
status[node] = SURFACE_DATA_IS_IN_QUAD3;
xx = -dx; yy = -dy;
}
}
/* Evaluate the Briggs coefficients */
surface_solve_Briggs_coefficients (C, C->Briggs[briggs_index].b, xx, yy, C->data[k].z);
briggs_index++;
}
}
}
}
GMT_LOCAL void surface_set_grid_parameters (struct SURFACE_INFO *C) {
/* Set the previous settings to the current settings */
C->previous_nx = C->current_nx;
C->previous_mx = C->current_mx;
C->previous_ny = C->current_ny;
/* Update the current parameters given the new C->current_stride setting */
C->info.current_nx = C->current_nx = (C->n_columns - 1) / C->current_stride + 1;
C->info.current_ny = C->current_ny = (C->n_rows - 1) / C->current_stride + 1;
C->current_mx = C->current_nx + 4;
C->current_mxmy = C->current_mx * (C->current_ny + 4); /* Only place where "my" is used */
C->info.inc[GMT_X] = C->inc[GMT_X] = C->current_stride * C->Grid->header->inc[GMT_X];
C->info.inc[GMT_Y] = C->inc[GMT_Y] = C->current_stride * C->Grid->header->inc[GMT_Y];
C->r_inc[GMT_X] = 1.0 / C->inc[GMT_X];
C->r_inc[GMT_Y] = 1.0 / C->inc[GMT_Y];
/* Update the grid node indices of the 4 corners */
C->node_nw_corner = 2 * C->current_mx + 2;
C->node_sw_corner = C->node_nw_corner + (C->current_ny - 1) * C->current_mx;
C->node_se_corner = C->node_sw_corner + C->current_nx - 1;
C->node_ne_corner = C->node_nw_corner + C->current_nx - 1;
}
GMT_LOCAL void surface_initialize_grid (struct GMT_CTRL *GMT, struct SURFACE_INFO *C) {
/* For the initial gridsize, compute weighted averages of data inside the search radius
* and assign the values to u[col,row], where col,row are multiples of gridsize.
* Weights are Gaussian, i.e., this is a MA Gaussian filter operation.
*/
uint64_t index_1, index_2, k, k_index, node;
int del_col, del_row, col, row, col_min, col_max, row_min, row_max, ki, kj;
double r, rfact, sum_w, sum_zw, weight, x0, y0;
gmt_grdfloat *u = C->Grid->data;
struct GMT_GRID_HEADER *h = C->Grid->header;
GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Initialize grid using moving average scheme [stride = %d]\n", C->current_stride);
del_col = irint (ceil (C->radius / C->inc[GMT_X]));
del_row = irint (ceil (C->radius / C->inc[GMT_Y]));
rfact = -4.5 / (C->radius*C->radius);
for (row = 0; row < C->current_ny; row++) {
y0 = row_to_y (row, h->wesn[YLO], h->wesn[YHI], C->inc[GMT_Y], C->current_ny);
for (col = 0; col < C->current_nx; col++) {
/* For this node on the grid, find all data points within the radius */
x0 = col_to_x (col, h->wesn[XLO], h->wesn[XHI], C->inc[GMT_X], C->current_nx);
col_min = col - del_col;
if (col_min < 0) col_min = 0;
col_max = col + del_col;
if (col_max >= C->current_nx) col_max = C->current_nx - 1;
row_min = row - del_row;
if (row_min < 0) row_min = 0;
row_max = row + del_row;
if (row_max >= C->current_ny) row_max = C->current_ny - 1;
index_1 = row_col_to_index (row_min, col_min, C->current_nx);
index_2 = row_col_to_index (row_max, col_max+1, C->current_nx);
sum_w = sum_zw = 0.0;
k = 0;
while (k < C->npoints && C->data[k].index < index_1) k++;
/* This double loop visits all nodes within the rectangle of dimensions (2*del_col by 2*del_row) centered on x0,y0 */
for (kj = row_min; k < C->npoints && kj <= row_max && C->data[k].index < index_2; kj++) {
for (ki = col_min; k < C->npoints && ki <= col_max && C->data[k].index < index_2; ki++) {
k_index = row_col_to_index (kj, ki, C->current_nx);
while (k < C->npoints && C->data[k].index < k_index) k++;
while (k < C->npoints && C->data[k].index == k_index) {
r = (C->data[k].x-x0)*(C->data[k].x-x0) + (C->data[k].y-y0)*(C->data[k].y-y0);
if (r > C->radius) continue; /* Outside the circle */
weight = exp (rfact * r);
sum_w += weight;
sum_zw += weight * C->data[k].z;
k++;
}
}
}
node = row_col_to_node (row, col, C->current_mx);
if (sum_w == 0.0) {
sprintf (C->format, "No data inside search radius at: %s %s [node set to data mean]\n", GMT->current.setting.format_float_out, GMT->current.setting.format_float_out);
GMT_Report (GMT->parent, GMT_MSG_WARNING, C->format, x0, y0);
u[node] = (gmt_grdfloat)C->z_mean;
}
else
u[node] = (gmt_grdfloat)(sum_zw/sum_w);
}
}
}
GMT_LOCAL int surface_read_data (struct GMT_CTRL *GMT, struct SURFACE_INFO *C, struct GMT_OPTION *options) {
/* Process input data into data structure */
int col, row, error;
uint64_t k = 0, kmax = 0, kmin = 0, n_dup = 0;
double *in, half_dx, zmin = DBL_MAX, zmax = -DBL_MAX, wesn_lim[4];
struct GMT_GRID_HEADER *h = C->Grid->header;
struct GMT_RECORD *In = NULL;
GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Processing input table data\n");
C->data = gmt_M_memory (GMT, NULL, C->n_alloc, struct SURFACE_DATA);
/* Read in xyz data and computes index no and store it in a structure */
if ((error = GMT_Set_Columns (GMT->parent, GMT_IN, 3, GMT_COL_FIX_NO_TEXT)) != GMT_NOERROR)
return (error);
if (GMT_Init_IO (GMT->parent, GMT_IS_DATASET, GMT_IS_POINT, GMT_IN, GMT_ADD_DEFAULT, 0, options) != GMT_NOERROR) /* Establishes data input */
return (GMT->parent->error);
C->z_mean = 0.0;
/* Initially allow points to be within 1 grid spacing of the grid */
wesn_lim[XLO] = h->wesn[XLO] - C->inc[GMT_X]; wesn_lim[XHI] = h->wesn[XHI] + C->inc[GMT_X];
wesn_lim[YLO] = h->wesn[YLO] - C->inc[GMT_Y]; wesn_lim[YHI] = h->wesn[YHI] + C->inc[GMT_Y];
half_dx = 0.5 * C->inc[GMT_X];
if (GMT_Begin_IO (GMT->parent, GMT_IS_DATASET, GMT_IN, GMT_HEADER_ON) != GMT_NOERROR) /* Enables data input and sets access mode */
return (GMT->parent->error);
do { /* Keep returning records until we reach EOF */
if ((In = GMT_Get_Record (GMT->parent, GMT_READ_DATA, NULL)) == NULL) { /* Read next record, get NULL if special case */
if (gmt_M_rec_is_error (GMT)) /* Bail if there are any read errors */
return (GMT_RUNTIME_ERROR);
else if (gmt_M_rec_is_eof (GMT)) /* Reached end of file */
break;
continue; /* Go back and read the next record */
}
if (In->data == NULL) {
gmt_quit_bad_record (GMT->parent, In);
return (GMT->parent->error);
}
/* Data record to process */
in = In->data; /* Only need to process numerical part here */
if (gmt_M_is_dnan (in[GMT_Z])) continue; /* Cannot use NaN values */
if (gmt_M_y_is_outside (GMT, in[GMT_Y], wesn_lim[YLO], wesn_lim[YHI])) continue; /* Outside y-range (or latitude) */
if (gmt_x_is_outside (GMT, &in[GMT_X], wesn_lim[XLO], wesn_lim[XHI])) continue; /* Outside x-range (or longitude) */
row = (int)y_to_row (in[GMT_Y], h->wesn[YLO], C->r_inc[GMT_Y], C->current_ny);
if (row < 0 || row >= C->current_ny) continue;
if (C->periodic && ((h->wesn[XHI]-in[GMT_X]) < half_dx)) { /* Push all values to the western nodes */
in[GMT_X] -= 360.0; /* Make this point constrain the western node value and then duplicate to east later */
col = 0;
}
else /* Regular point not at the periodic boundary */
col = (int)x_to_col (in[GMT_X], h->wesn[XLO], C->r_inc[GMT_X]);
if (col < 0 || col >= C->current_nx) continue;
C->data[k].index = row_col_to_index (row, col, C->current_nx);
C->data[k].x = (gmt_grdfloat)in[GMT_X];
C->data[k].y = (gmt_grdfloat)in[GMT_Y];
C->data[k].z = (gmt_grdfloat)in[GMT_Z];
/* Determine the mean, min and max z-values */
if (zmin > in[GMT_Z]) zmin = in[GMT_Z], kmin = k;
if (zmax < in[GMT_Z]) zmax = in[GMT_Z], kmax = k;
C->z_mean += in[GMT_Z];
if (++k == C->n_alloc) {
C->n_alloc <<= 1;
C->data = gmt_M_memory (GMT, C->data, C->n_alloc, struct SURFACE_DATA);
}
if (C->periodic && col == 0) { /* Now we must replicate information from the western to the eastern boundary */
col = C->current_nx - 1;
C->data[k].index = row_col_to_index (row, col, C->current_nx);
C->data[k].x = (gmt_grdfloat)(in[GMT_X] + 360.0);
C->data[k].y = (gmt_grdfloat)in[GMT_Y];
C->data[k].z = (gmt_grdfloat)in[GMT_Z];
C->z_mean += in[GMT_Z];
if (++k == C->n_alloc) {
C->n_alloc <<= 1;
C->data = gmt_M_memory (GMT, C->data, C->n_alloc, struct SURFACE_DATA);
}
n_dup++;
}
} while (true);
if (GMT_End_IO (GMT->parent, GMT_IN, 0) != GMT_NOERROR) /* Disables further data input */
return (GMT->parent->error);
C->npoints = k; /* Number of data points that passed being "inside" the grid region */
if (C->npoints == 0) {
GMT_Report (GMT->parent, GMT_MSG_ERROR, "No datapoints inside region, aborting\n");
gmt_M_free (GMT, C->data);
return (GMT_RUNTIME_ERROR);
}
C->z_mean /= C->npoints; /* Estimate mean data value */
if (gmt_M_is_verbose (GMT, GMT_MSG_INFORMATION)) {
char msg[GMT_LEN256] = {""};
sprintf (C->format, "%s %s %s\n", GMT->current.setting.format_float_out, GMT->current.setting.format_float_out, GMT->current.setting.format_float_out);
sprintf (msg, C->format, (double)C->data[kmin].x, (double)C->data[kmin].y, (double)C->data[kmin].z);
GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Minimum value of your dataset x,y,z at: %s", msg);
sprintf (msg, C->format, (double)C->data[kmax].x, (double)C->data[kmax].y, (double)C->data[kmax].z);
GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Maximum value of your dataset x,y,z at: %s", msg);
if (C->periodic && n_dup) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Number of input values shared between repeating west and east column nodes: %" PRIu64 "\n", n_dup);
}
C->data = gmt_M_memory (GMT, C->data, C->npoints, struct SURFACE_DATA);
if (C->set_limit[LO] == DATA) /* Wanted to set lower limit based on minimum observed z value */
C->limit[LO] = C->data[kmin].z;
else if (C->set_limit[LO] == VALUE && C->limit[LO] > C->data[kmin].z)
GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Your lower value is > than min data value.\n");
if (C->set_limit[HI] == DATA) /* Wanted to set upper limit based on maximum observed z value */
C->limit[HI] = C->data[kmax].z;
else if (C->set_limit[HI] == VALUE && C->limit[HI] < C->data[kmax].z)
GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Your upper value is < than max data value.\n");
return (0);
}
GMT_LOCAL void surface_set_NaN (struct GMT_CTRL *GMT, struct GMT_GRID *G, unsigned int r0, unsigned int r1, unsigned int c0, unsigned int c1) {
/* Set a subset of a grid to NaN based on rows and columns specified, inclusive.
* e.g, in Matlab this would be G->data(r0:r1,c0:c1) = NaN */
uint64_t ij = gmt_M_ijp (G->header, r0, c0); /* Top left node in block */
unsigned int r, c; /* Row and column loop variables */
for (r = r0; r <= r1; r++) /* For all the rows to work on: r0 up to and including r1 */
for (c = c0; c <= c1; c++) /* For all the columns to work on: c0 up top and including c1 */
G->data[ij+(r-r0)*G->header->mx+(c-c0)] = GMT->session.f_NaN;
}
GMT_LOCAL void surface_enlarge_constraint_grid (struct GMT_CTRL *GMT, struct SURFACE_INFO *C, struct GMT_GRID *G) {
/* We must enlarge the grid region after having read the grid as is. Then we need to set the new nodes
* not part of the constraint file to NaN. */
gmt_grd_pad_on (GMT, G, C->q_pad); /* First add in the larger pad to adjust the size of the grid */
gmt_M_memcpy (G->header->wesn, C->Grid->header->wesn, 4U, double); /* Next enlarge the region in the header */
gmt_M_grd_setpad (GMT, G->header, C->Grid->header->pad); /* Reset to standard pad (2/2/2/2) */
gmt_set_grddim (GMT, G->header); /* Update all dimensions to reflect the revised region and pad*/
/* Now the grid has the right shape as the interior surface solution grid. We now need to set the new
* nodes to NaN - to do this we consult C->q_pad[side] > 2 */
if (C->q_pad[XLO] > 2) /* We extended the grid westwards so need to set the first columns on the left to NaN */
surface_set_NaN (GMT, G, 0, G->header->n_rows - 1, 0, C->q_pad[XLO] - 3);
if (C->q_pad[XHI] > 2) /* We extended the grid eastwards so need to set the last columns on the right to NaN */
surface_set_NaN (GMT, G, 0, G->header->n_rows - 1, G->header->n_columns - C->q_pad[XHI] + 2, G->header->n_columns - 1);
if (C->q_pad[YLO] > 2) /* We extended the grid southwards so need to set the last rows on the bottom to NaN */
surface_set_NaN (GMT, G, G->header->n_rows - C->q_pad[YLO] + 2, G->header->n_rows - 1, 0, G->header->n_columns - 1);
if (C->q_pad[YHI] > 2) /* We extended the grid northwards so need to set the first rows on the top to NaN */
surface_set_NaN (GMT, G, 0, C->q_pad[YHI] - 3, 0, G->header->n_columns - 1);
}
GMT_LOCAL int surface_load_constraints (struct GMT_CTRL *GMT, struct SURFACE_INFO *C, int transform) {
/* Deal with the constants or grids supplied via -L. Note: Because we remove a
* best-fitting plane from the data, even a simple constant constraint will become
* a plane and thus must be represented on a grid. */
unsigned int end, col, row;
uint64_t node;
char *limit[2] = {"Lower", "Upper"};
double y_up;
struct GMTAPI_CTRL *API = GMT->parent;
GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Load any data constraint limit grids\n");
/* Load lower/upper limits, verify range, deplane, and rescale */
for (end = LO; end <= HI; end++) {
if (C->set_limit[end] == NONE) continue; /* Nothing desired */
if (C->set_limit[end] < SURFACE) { /* Got a constant level for this end */
if ((C->Bound[end] = GMT_Duplicate_Data (API, GMT_IS_GRID, GMT_DUPLICATE_ALLOC, C->Grid)) == NULL) return (API->error);
for (node = 0; node < C->mxmy; node++) C->Bound[end]->data[node] = (gmt_grdfloat)C->limit[end];
}
else { /* Got a grid with a surface */
if ((C->Bound[end] = GMT_Read_Data (GMT->parent, GMT_IS_GRID, GMT_IS_FILE, GMT_IS_SURFACE, GMT_CONTAINER_ONLY, NULL, C->limit_file[end], NULL)) == NULL) return (API->error); /* Get header only */
if (!C->adjusted && (C->Bound[end]->header->n_columns != C->Grid->header->n_columns || C->Bound[end]->header->n_rows != C->Grid->header->n_rows)) {
GMT_Report (API, GMT_MSG_ERROR, "%s limit file not of proper dimensions!\n", limit[end]);
return (GMT_RUNTIME_ERROR);
}
if (GMT_Read_Data (GMT->parent, GMT_IS_GRID, GMT_IS_FILE, GMT_IS_SURFACE, GMT_DATA_ONLY, NULL, C->limit_file[end], C->Bound[end]) == NULL) return (API->error);
if (C->adjusted) /* Must adjust padding and region and set new nodes to NaN */
surface_enlarge_constraint_grid (GMT, C, C->Bound[end]);
}
if (transform) { /* Remove best-fitting plane and normalize the bounding values */
for (row = 0; row < C->Grid->header->n_rows; row++) {
y_up = (double)(C->Grid->header->n_rows - row - 1); /* Require y_up = 0 at south and positive toward north */
node = row_col_to_node (row, 0, C->current_mx);
for (col = 0; col < C->Grid->header->n_columns; col++, node++) {
if (gmt_M_is_fnan (C->Bound[end]->data[node])) continue;
C->Bound[end]->data[node] -= (gmt_grdfloat)evaluate_plane (C, col, y_up); /* Remove plane */
C->Bound[end]->data[node] *= (gmt_grdfloat)C->r_z_rms; /* Normalize residuals */
}
}
}
C->constrained = true; /* At least one of the limits will be constrained */
if (C->Bh == NULL) C->Bh = C->Bound[end]->header; /* Just pick either one of them */
}
return (0);
}
GMT_LOCAL int surface_write_grid (struct GMT_CTRL *GMT, struct SURFACE_CTRL *Ctrl, struct SURFACE_INFO *C, char *grdfile) {
/* Write output grid to file */
uint64_t node;
openmp_int row, col;
int err, end;
char *limit[2] = {"lower", "upper"};
gmt_grdfloat *u = C->Grid->data;
if (!Ctrl->Q.active && Ctrl->Q.adjusted) { /* Probably need to shrink region to the desired one by increasing the pads */
int del_pad[4] = {0, 0, 0, 0}, k, n = 0;
struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (C->Grid->header);
/* Determine the shifts inwards for each side */
del_pad[XLO] = irint ((C->wesn_orig[XLO] - C->Grid->header->wesn[XLO]) * HH->r_inc[GMT_X]);
del_pad[XHI] = irint ((C->Grid->header->wesn[XHI] - C->wesn_orig[XHI]) * HH->r_inc[GMT_X]);
del_pad[YLO] = irint ((C->wesn_orig[YLO] - C->Grid->header->wesn[YLO]) * HH->r_inc[GMT_Y]);
del_pad[YHI] = irint ((C->Grid->header->wesn[YHI] - C->wesn_orig[YHI]) * HH->r_inc[GMT_Y]);
for (k = 0; k < 4; k++) n += abs (del_pad[k]); /* See if any is needed */
if (n) { /* Yep, must update pad and all meta data for this grid first */
GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Increase pad by %d %d %d %d\n", del_pad[XLO], del_pad[XHI], del_pad[YLO], del_pad[YHI]);
for (k = 0; k < 4; k++) C->Grid->header->pad[k] += del_pad[k]; /* Increase pad to shrink region */
gmt_M_memcpy (C->Grid->header->wesn, C->wesn_orig, 4U, double); /* Reset -R to what was requested */
gmt_set_grddim (GMT, C->Grid->header); /* Update dimensions given the change of pad */
}
}
GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Prepare final output grid [stride = %d]\n", C->current_stride);
strcpy (C->Grid->header->title, "Data gridded with continuous surface splines in tension");
if (GMT->common.R.registration == GMT_GRID_PIXEL_REG) { /* Pixel registration request. Reset region to the original extent */
gmt_M_memcpy (C->Grid->header->wesn, C->wesn_orig, 4, double);
C->Grid->header->registration = GMT->common.R.registration;
/* Must reduce both n_columns,n_rows by 1 and make the easternmost column and northernmost row part of the grid pad */
C->Grid->header->n_columns--; C->n_columns--;
C->Grid->header->n_rows--; C->n_rows--;
C->Grid->header->pad[XHI]++; /* Presumably increase pad from 2 to 3 */
C->Grid->header->pad[YHI]++; /* Presumably increase pad from 2 to 3 */
gmt_set_grddim (GMT, C->Grid->header); /* Reset all integer dimensions and xy_off */
}
if (C->constrained) { /* Must check that we don't exceed any imposed limits. */
/* Reload the constraints, but this time do not transform the data */
if ((err = surface_load_constraints (GMT, C, false)) != 0) return (err);
gmt_M_grd_loop (GMT, C->Grid, row, col, node) { /* Make sure we clip to the specified bounds */
if (C->set_limit[LO] && !gmt_M_is_fnan (C->Bound[LO]->data[node]) && u[node] < C->Bound[LO]->data[node]) u[node] = C->Bound[LO]->data[node];
if (C->set_limit[HI] && !gmt_M_is_fnan (C->Bound[HI]->data[node]) && u[node] > C->Bound[HI]->data[node]) u[node] = C->Bound[HI]->data[node];
}
/* Free any bounding surfaces */
for (end = LO; end <= HI; end++) {
if ((C->set_limit[end] > NONE && C->set_limit[end] < SURFACE) && GMT_Destroy_Data (GMT->parent, &C->Bound[end]) != GMT_NOERROR) {
GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failed to free %s boundary\n", limit[end]);
}
}
}
if (C->periodic) { /* Ensure exact periodicity at E-W boundaries */
for (row = 0; row < (openmp_int)C->current_ny; row++) {
node = row_col_to_node (row, 0, C->current_mx);
u[node] = u[node+C->current_nx-1] = (gmt_grdfloat)(0.5 * (u[node] + u[node+C->current_nx-1])); /* Set these to the same as their average */
}
}
/* Time to write our final grid */
if (GMT_Write_Data (GMT->parent, GMT_IS_GRID, GMT_IS_FILE, GMT_IS_SURFACE, GMT_CONTAINER_AND_DATA, NULL, grdfile, C->Grid) != GMT_NOERROR)