grid.c 146 KB
Newer Older
Uwe Schulzweida's avatar
Uwe Schulzweida committed
1
2
3
4
#if defined (HAVE_CONFIG_H)
#  include "config.h"
#endif

Thomas Jahns's avatar
Thomas Jahns committed
5
#include <assert.h>
Uwe Schulzweida's avatar
Uwe Schulzweida committed
6
#include <string.h>
7

Uwe Schulzweida's avatar
Uwe Schulzweida committed
8
9
#include "dmemory.h"
#include "cdi.h"
10
#include "cdi_cksum.h"
11
#include "cdi_int.h"
12
#include "cdi_uuid.h"
Uwe Schulzweida's avatar
Uwe Schulzweida committed
13
#include "grid.h"
14
#include "gaussgrid.h"
15
#include "resource_handle.h"
16
#include "resource_unpack.h"
17
#include "namespace.h"
18
#include "serialize.h"
19
20
#include "vlist.h"

21
double grid_missval = -9999.;
22
int (*proj_lonlat_to_lcc_func)() = NULL;
23
int (*proj_lcc_to_lonlat_func)() = NULL;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
24

25
26
27
/* the value in the second pair of brackets must match the length of
 * the longest string (including terminating NUL) */
static const char Grids[][17] = {
28
29
30
31
32
33
34
35
36
  /*  0 */  "undefined",
  /*  1 */  "generic",
  /*  2 */  "gaussian",
  /*  3 */  "gaussian reduced",
  /*  4 */  "lonlat",
  /*  5 */  "spectral",
  /*  6 */  "fourier",
  /*  7 */  "gme",
  /*  8 */  "trajectory",
37
  /*  9 */  "unstructured",
38
39
  /* 10 */  "curvilinear",
  /* 11 */  "lcc",
40
  /* 12 */  "projection",
Uwe Schulzweida's avatar
Uwe Schulzweida committed
41
  /* 13 */  "characterXY",
Uwe Schulzweida's avatar
Uwe Schulzweida committed
42
43
};

44
45
46
47
48
/* must match table below */
enum xystdname_idx {
  grid_xystdname_grid_latlon,
  grid_xystdname_latlon,
  grid_xystdname_projection,
Uwe Schulzweida's avatar
Uwe Schulzweida committed
49
  grid_xystdname_char,
50
51
52
53
54
55
56
57
};
static const char xystdname_tab[][2][24] = {
  [grid_xystdname_grid_latlon] = { "grid_longitude",
                                   "grid_latitude" },
  [grid_xystdname_latlon] = { "longitude",
                              "latitude" },
  [grid_xystdname_projection] = { "projection_x_coordinate",
                                  "projection_y_coordinate" },
Uwe Schulzweida's avatar
Uwe Schulzweida committed
58
59
  [grid_xystdname_char] = { "region",
                            "region" },
60
61
62
};


Uwe Schulzweida's avatar
Uwe Schulzweida committed
63

64
65
66
static int    gridCompareP    ( void * gridptr1, void * gridptr2 );
static void   gridDestroyP    ( void * gridptr );
static void   gridPrintP      ( void * gridptr, FILE * fp );
67
static int    gridGetPackSize ( void * gridptr, void *context);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
68
static void   gridPack        ( void * gridptr, void * buff, int size, int *position, void *context);
Deike Kleberg's avatar
minimal    
Deike Kleberg committed
69
static int    gridTxCode      ( void );
Uwe Schulzweida's avatar
Uwe Schulzweida committed
70

71
static const resOps gridOps = {
72
73
74
75
76
77
  gridCompareP,
  gridDestroyP,
  gridPrintP,
  gridGetPackSize,
  gridPack,
  gridTxCode
78
};
Uwe Schulzweida's avatar
Uwe Schulzweida committed
79

80
static int  GRID_Debug = 0;   /* If set to 1, debugging */
Uwe Schulzweida's avatar
Uwe Schulzweida committed
81

Uwe Schulzweida's avatar
Uwe Schulzweida committed
82

83
grid_t *grid_to_pointer(int gridID)
84
85
86
{
  return (grid_t *)reshGetVal(gridID, &gridOps);
}
Uwe Schulzweida's avatar
Uwe Schulzweida committed
87

88
#define gridMark4Update(gridID) reshSetStatus(gridID, &gridOps, RESH_DESYNC_IN_USE)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
89

90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
static
bool cdiInqAttConvertedToFloat(int gridID, int atttype, const char *attname, int attlen, double *attflt)
{
  bool status = true;

  if ( atttype == CDI_DATATYPE_INT32 )
    {
      int attint[attlen];
      cdiInqAttInt(gridID, CDI_GLOBAL, attname, attlen, attint);
      for ( int i = 0; i < attlen; ++i ) attflt[i] = (double)attint[i];
    }
  else if ( atttype == CDI_DATATYPE_FLT32 || atttype == CDI_DATATYPE_FLT64 )
    {
      cdiInqAttFlt(gridID, CDI_GLOBAL, attname, attlen, attflt);
    }
  else
    {
      status = false;
    }

  return status;
}

113

Deike Kleberg's avatar
Deike Kleberg committed
114
void grid_init(grid_t *gridptr)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
115
{
116
117
118
  gridptr->self          = CDI_UNDEFID;
  gridptr->type          = CDI_UNDEFID;
  gridptr->proj          = CDI_UNDEFID;
119
  gridptr->projtype      = CDI_UNDEFID;
120
121
  gridptr->mask          = NULL;
  gridptr->mask_gme      = NULL;
122
  gridptr->x.vals        = NULL;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
123
124
  gridptr->x.cvals       = NULL;
  gridptr->x.clength     = 0;
125
  gridptr->y.vals        = NULL;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
126
127
  gridptr->y.cvals       = NULL;
  gridptr->y.clength     = 0;
128
129
  gridptr->x.bounds      = NULL;
  gridptr->y.bounds      = NULL;
130
  gridptr->area          = NULL;
131
132
  gridptr->rowlon        = NULL;
  gridptr->nrowlon       = 0;
133

134
135
136
137
138
139
  gridptr->x.first       = 0.0;
  gridptr->x.last        = 0.0;
  gridptr->x.inc         = 0.0;
  gridptr->y.first       = 0.0;
  gridptr->y.last        = 0.0;
  gridptr->y.inc         = 0.0;
140

141
142
143
144
  gridptr->gme.nd        = 0;
  gridptr->gme.ni        = 0;
  gridptr->gme.ni2       = 0;
  gridptr->gme.ni3       = 0;
145

146
147
148
149
150
151
152
  gridptr->trunc         = 0;
  gridptr->nvertex       = 0;
  gridptr->number        = 0;
  gridptr->position      = 0;
  gridptr->reference     = NULL;
  gridptr->prec          = 0;
  gridptr->size          = 0;
153
154
  gridptr->x.size        = 0;
  gridptr->y.size        = 0;
155
  gridptr->np            = 0;
156
157
  gridptr->x.flag        = 0;
  gridptr->y.flag        = 0;
158
  gridptr->isCyclic      = CDI_UNDEFID;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
159

160
  gridptr->lcomplex      = false;
161
162
163
164
165
166
167
168
169
170
171
  gridptr->hasdims       = true;
  gridptr->x.dimname[0]  = 0;
  gridptr->y.dimname[0]  = 0;
  gridptr->x.name[0]     = 0;
  gridptr->y.name[0]     = 0;
  gridptr->x.longname[0] = 0;
  gridptr->y.longname[0] = 0;
  gridptr->x.units[0]    = 0;
  gridptr->y.units[0]    = 0;
  gridptr->x.stdname     = NULL;
  gridptr->y.stdname     = NULL;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
172
173
174
  gridptr->vdimname[0]   = 0;
  gridptr->mapname[0]    = 0;
  gridptr->mapping[0]    = 0;
Thomas Jahns's avatar
Thomas Jahns committed
175
  memset(gridptr->uuid, 0, CDI_UUID_SIZE);
176
177
  gridptr->name          = NULL;
  gridptr->vtable        = &cdiGridVtable;
178
  gridptr->atts.nalloc   = MAX_ATTRIBUTES;
179
  gridptr->atts.nelems   = 0;
180
  gridptr->uvRelativeToGrid      = 0;   // Some models deliver wind U,V relative to the grid-cell
181
182
183
184
185
186
187
188
189
190
  gridptr->iScansNegatively      = 0;
  gridptr->jScansPositively      = 1;
  gridptr->jPointsAreConsecutive = 0;
  gridptr->scanningMode          = 128*gridptr->iScansNegatively + 64*gridptr->jScansPositively + 32*gridptr->jPointsAreConsecutive;
  /* scanningMode  = 128 * iScansNegatively + 64 * jScansPositively + 32 * jPointsAreConsecutive;
               64  = 128 * 0                + 64 *        1         + 32 * 0
               00  = 128 * 0                + 64 *        0         + 32 * 0
               96  = 128 * 0                + 64 *        1         + 32 * 1
     Default / implicit scanning mode is 64:
                        i and j scan positively, i points are consecutive (row-major)        */
Uwe Schulzweida's avatar
Uwe Schulzweida committed
191
192
}

193

194
195
static
void grid_free_components(grid_t *gridptr)
Deike Kleberg's avatar
Deike Kleberg committed
196
{
197
  void *p2free[] = { gridptr->mask, gridptr->mask_gme,
198
                     gridptr->x.vals, gridptr->y.vals,
Uwe Schulzweida's avatar
Uwe Schulzweida committed
199
                     gridptr->x.cvals, gridptr->y.cvals,
200
201
202
203
                     gridptr->x.bounds, gridptr->y.bounds,
                     gridptr->rowlon, gridptr->area,
                     gridptr->reference, gridptr->name};

204
  for ( size_t i = 0; i < sizeof(p2free)/sizeof(p2free[0]); ++i )
205
    if ( p2free[i] ) Free(p2free[i]);
206
}
Deike Kleberg's avatar
Deike Kleberg committed
207

208
209
210
void grid_free(grid_t *gridptr)
{
  grid_free_components(gridptr);
Deike Kleberg's avatar
Deike Kleberg committed
211
212
213
  grid_init(gridptr);
}

214
215
static
grid_t *gridNewEntry(cdiResH resH)
216
{
217
  grid_t *gridptr = (grid_t*) Malloc(sizeof(grid_t));
218
  grid_init(gridptr);
219

220
  if ( resH == CDI_UNDEFID )
221
222
223
224
225
226
    gridptr->self = reshPut(gridptr, &gridOps);
  else
    {
      gridptr->self = resH;
      reshReplace(resH, gridptr, &gridOps);
    }
227

228
  return gridptr;
229
230
}

231
static
232
void gridInit(void)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
233
{
234
  static bool gridInitialized = false;
235
  if ( gridInitialized ) return;
236
  gridInitialized = true;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
237

Uwe Schulzweida's avatar
Uwe Schulzweida committed
238
  const char *env = getenv("GRID_DEBUG");
239
  if ( env ) GRID_Debug = atoi(env);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
240
241
}

242
243
static
void grid_copy_base_scalar_fields(grid_t *gridptrOrig, grid_t *gridptrDup)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
244
{
Thomas Jahns's avatar
Thomas Jahns committed
245
246
  memcpy(gridptrDup, gridptrOrig, sizeof(grid_t));
  gridptrDup->self = CDI_UNDEFID;
247
  if ( gridptrOrig->reference )
Thomas Jahns's avatar
Thomas Jahns committed
248
249
250
    gridptrDup->reference = strdupx(gridptrOrig->reference);
}

Uwe Schulzweida's avatar
Uwe Schulzweida committed
251

252
253
static
grid_t *grid_copy_base(grid_t *gridptrOrig)
Thomas Jahns's avatar
Thomas Jahns committed
254
255
256
257
258
{
  grid_t *gridptrDup = (grid_t *)Malloc(sizeof (*gridptrDup));
  gridptrOrig->vtable->copyScalarFields(gridptrOrig, gridptrDup);
  gridptrOrig->vtable->copyArrayFields(gridptrOrig, gridptrDup);
  return gridptrDup;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
259
260
}

Uwe Schulzweida's avatar
Uwe Schulzweida committed
261

262
unsigned cdiGridCount(void)
Thomas Jahns's avatar
Thomas Jahns committed
263
{
264
  return reshCountType(&gridOps);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
265
266
}

267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
static inline
void gridSetString(char *gridstrname, const char *name, size_t len)
{
  if ( len > CDI_MAX_NAME ) len = CDI_MAX_NAME;
  strncpy(gridstrname, name, len);
  gridstrname[len - 1] = 0;
}

static inline
void gridGetString(char *name, const char *gridstrname, size_t len)
{
  if ( len > CDI_MAX_NAME ) len = CDI_MAX_NAME;
  strncpy(name, gridstrname, len);
  name[len - 1] = 0;
}

283
284
static inline
void gridSetName(char *gridstrname, const char *name)
285
{
286
287
  strncpy(gridstrname, name, CDI_MAX_NAME);
  gridstrname[CDI_MAX_NAME - 1] = 0;
288
289
}

290

291
void cdiGridTypeInit(grid_t *gridptr, int gridtype, size_t size)
292
293
294
295
{
  gridptr->type = gridtype;
  gridptr->size = size;

296
297
298
  if      ( gridtype == GRID_CURVILINEAR  ) gridptr->nvertex = 4;
  else if ( gridtype == GRID_UNSTRUCTURED ) gridptr->x.size = size;

299
300
301
302
303
304
  switch (gridtype)
    {
    case GRID_LONLAT:
    case GRID_GAUSSIAN:
    case GRID_GAUSSIAN_REDUCED:
    case GRID_TRAJECTORY:
305
306
307
    case GRID_CURVILINEAR:
    case GRID_UNSTRUCTURED:
    case GRID_GME:
308
309
310
      {
        if ( gridtype == GRID_TRAJECTORY )
          {
311
312
            if ( gridptr->x.name[0] == 0 ) gridSetName(gridptr->x.name, "tlon");
            if ( gridptr->y.name[0] == 0 ) gridSetName(gridptr->y.name, "tlat");
313
314
315
          }
        else
          {
316
317
            if ( gridptr->x.name[0] == 0 ) gridSetName(gridptr->x.name, "lon");
            if ( gridptr->y.name[0] == 0 ) gridSetName(gridptr->y.name, "lat");
318
          }
319

320
321
        gridSetName(gridptr->x.longname, "longitude");
        gridSetName(gridptr->y.longname, "latitude");
322

323
324
        gridptr->x.stdname = xystdname_tab[grid_xystdname_latlon][0];
        gridptr->y.stdname = xystdname_tab[grid_xystdname_latlon][1];
325
326
        gridSetName(gridptr->x.units, "degrees_east");
        gridSetName(gridptr->y.units, "degrees_north");
327

328
329
        break;
      }
Uwe Schulzweida's avatar
Uwe Schulzweida committed
330
331
332
333
334
335
336
    case GRID_CHARXY:
      {
        if ( gridptr->x.cvals )
          gridptr->x.stdname = xystdname_tab[grid_xystdname_char][0];
        if ( gridptr->y.cvals )
          gridptr->y.stdname = xystdname_tab[grid_xystdname_char][0];
      }
337
    case GRID_GENERIC:
338
    case GRID_PROJECTION:
339
      {
340
341
        if ( gridptr->x.name[0] == 0 ) gridSetName(gridptr->x.name, "x");
        if ( gridptr->y.name[0] == 0 ) gridSetName(gridptr->y.name, "y");
342
343
344
        if ( gridtype == GRID_PROJECTION )
          {
            gridSetName(gridptr->mapname, "Projection");
345

346
347
348
349
350
            gridptr->x.stdname = xystdname_tab[grid_xystdname_projection][0];
            gridptr->y.stdname = xystdname_tab[grid_xystdname_projection][1];
            gridSetName(gridptr->x.units, "m");
            gridSetName(gridptr->y.units, "m");
          }
351
352
353
354
355
356
        break;
      }
    }
}


357
// used also in CDO
358
void gridGenXvals(int xsize, double xfirst, double xlast, double xinc, double *restrict xvals)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
359
{
360
  if ( (! (fabs(xinc) > 0)) && xsize > 1 )
Uwe Schulzweida's avatar
Uwe Schulzweida committed
361
362
    {
      if ( xfirst >= xlast )
Thomas Jahns's avatar
Thomas Jahns committed
363
364
365
366
        {
          while ( xfirst >= xlast ) xlast += 360;
          xinc = (xlast-xfirst)/(xsize);
        }
Uwe Schulzweida's avatar
Uwe Schulzweida committed
367
      else
Thomas Jahns's avatar
Thomas Jahns committed
368
369
370
        {
          xinc = (xlast-xfirst)/(xsize-1);
        }
Uwe Schulzweida's avatar
Uwe Schulzweida committed
371
372
    }

373
  for ( int i = 0; i < xsize; ++i )
Uwe Schulzweida's avatar
Uwe Schulzweida committed
374
375
376
    xvals[i] = xfirst + i*xinc;
}

377
static
378
void calc_gaussgrid(double *restrict yvals, size_t ysize, double yfirst, double ylast)
379
{
380
381
  double *restrict yw = (double *) Malloc(ysize * sizeof(double));
  gaussaw(yvals, yw, ysize);
382
  Free(yw);
383
  for (size_t i = 0; i < ysize; i++ )
384
385
386
387
    yvals[i] = asin(yvals[i])/M_PI*180.0;

  if ( yfirst < ylast && yfirst > -90.0 && ylast < 90.0 )
    {
388
389
      size_t yhsize = ysize/2;
      for (size_t i = 0; i < yhsize; i++ )
Thomas Jahns's avatar
Thomas Jahns committed
390
        {
391
          double ytmp = yvals[i];
Thomas Jahns's avatar
Thomas Jahns committed
392
393
394
          yvals[i] = yvals[ysize-i-1];
          yvals[ysize-i-1] = ytmp;
        }
395
396
397
    }
}

398
// used also in CDO
Thomas Jahns's avatar
Thomas Jahns committed
399
void gridGenYvals(int gridtype, int ysize, double yfirst, double ylast, double yinc, double *restrict yvals)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
400
{
401
  const double deleps = 0.002;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
402
403
404
405

  if ( gridtype == GRID_GAUSSIAN || gridtype == GRID_GAUSSIAN_REDUCED )
    {
      if ( ysize > 2 )
Deike Kleberg's avatar
Deike Kleberg committed
406
407
408
409
410
411
	{
	  calc_gaussgrid(yvals, ysize, yfirst, ylast);

	  if ( ! (IS_EQUAL(yfirst, 0) && IS_EQUAL(ylast, 0)) )
	    if ( fabs(yvals[0] - yfirst) > deleps || fabs(yvals[ysize-1] - ylast) > deleps )
	      {
412
		double *restrict ytmp = NULL;
413
414
		int nstart;
                bool lfound = false;
415
		int ny = (int) (180./(fabs(ylast-yfirst)/(ysize-1)) + 0.5);
Deike Kleberg's avatar
Deike Kleberg committed
416
417
418
		ny -= ny%2;
		if ( ny > ysize && ny < 4096 )
		  {
419
		    ytmp = (double *) Malloc((size_t)ny * sizeof (double));
Deike Kleberg's avatar
Deike Kleberg committed
420
		    calc_gaussgrid(ytmp, ny, yfirst, ylast);
421
422
423
424
425
426
                    {
                      int i;
                      for ( i = 0; i < (ny-ysize); i++ )
                        if ( fabs(ytmp[i] - yfirst) < deleps ) break;
                      nstart = i;
                    }
Deike Kleberg's avatar
Deike Kleberg committed
427

428
429
		    lfound = (nstart+ysize-1) < ny
                      && fabs(ytmp[nstart+ysize-1] - ylast) < deleps;
430
431
432
433
                    if ( lfound )
                      {
                        for (int i = 0; i < ysize; i++) yvals[i] = ytmp[i+nstart];
                      }
Deike Kleberg's avatar
Deike Kleberg committed
434
435
		  }

436
		if ( !lfound )
Deike Kleberg's avatar
Deike Kleberg committed
437
		  {
438
		    Warning("Cannot calculate gaussian latitudes for lat1 = %g latn = %g!", yfirst, ylast);
439
		    for (int i = 0; i < ysize; i++ ) yvals[i] = 0;
Deike Kleberg's avatar
Deike Kleberg committed
440
441
442
443
		    yvals[0] = yfirst;
		    yvals[ysize-1] = ylast;
		  }

444
		if ( ytmp ) Free(ytmp);
Deike Kleberg's avatar
Deike Kleberg committed
445
446
	      }
	}
Uwe Schulzweida's avatar
Uwe Schulzweida committed
447
      else
Thomas Jahns's avatar
Thomas Jahns committed
448
449
450
451
        {
          yvals[0] = yfirst;
          yvals[ysize-1] = ylast;
        }
Uwe Schulzweida's avatar
Uwe Schulzweida committed
452
453
454
455
    }
  /*     else if ( gridtype == GRID_LONLAT || gridtype == GRID_GENERIC ) */
  else
    {
456
      if ( (! (fabs(yinc) > 0)) && ysize > 1 )
Thomas Jahns's avatar
Thomas Jahns committed
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
        {
          if ( IS_EQUAL(yfirst, ylast) && IS_NOT_EQUAL(yfirst, 0) ) ylast *= -1;

          if ( yfirst > ylast )
            yinc = (yfirst-ylast)/(ysize-1);
          else if ( yfirst < ylast )
            yinc = (ylast-yfirst)/(ysize-1);
          else
            {
              if ( ysize%2 != 0 )
                {
                  yinc = 180.0/(ysize-1);
                  yfirst = -90;
                }
              else
                {
                  yinc = 180.0/ysize;
                  yfirst = -90 + yinc/2;
                }
            }
        }
Uwe Schulzweida's avatar
Uwe Schulzweida committed
478
479
480

      if ( yfirst > ylast && yinc > 0 ) yinc = -yinc;

481
      for (int i = 0; i < ysize; i++ )
Thomas Jahns's avatar
Thomas Jahns committed
482
        yvals[i] = yfirst + i*yinc;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
483
484
485
    }
  /*
    else
486
    Error("unable to calculate values for %s grid!", gridNamePtr(gridtype));
Uwe Schulzweida's avatar
Uwe Schulzweida committed
487
488
489
490
  */
}

/*
491
@Function  gridCreate
492
@Title     Create a horizontal Grid
Uwe Schulzweida's avatar
Uwe Schulzweida committed
493

494
@Prototype int gridCreate(int gridtype, size_t size)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
495
496
@Parameter
    @Item  gridtype  The type of the grid, one of the set of predefined CDI grid types.
497
                     The valid CDI grid types are @func{GRID_GENERIC}, @func{GRID_GAUSSIAN},
498
                     @func{GRID_LONLAT}, @func{GRID_PROJECTION}, @func{GRID_SPECTRAL},
499
                     @func{GRID_GME}, @func{GRID_CURVILINEAR} and @func{GRID_UNSTRUCTURED}.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
500
501
502
    @Item  size      Number of gridpoints.

@Description
503
The function @func{gridCreate} creates a horizontal Grid.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
504
505

@Result
506
@func{gridCreate} returns an identifier to the Grid.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
507
508

@Example
509
Here is an example using @func{gridCreate} to create a regular lon/lat Grid:
Uwe Schulzweida's avatar
Uwe Schulzweida committed
510
511

@Source
Uwe Schulzweida's avatar
Uwe Schulzweida committed
512
513
#include "cdi.h"
   ...
514
515
#define  nlon  12
#define  nlat   6
Uwe Schulzweida's avatar
Uwe Schulzweida committed
516
   ...
517
518
double lons[nlon] = {0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330};
double lats[nlat] = {-75, -45, -15, 15, 45, 75};
Uwe Schulzweida's avatar
Uwe Schulzweida committed
519
520
int gridID;
   ...
521
522
523
gridID = gridCreate(GRID_LONLAT, nlon*nlat);
gridDefXsize(gridID, nlon);
gridDefYsize(gridID, nlat);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
524
525
526
527
gridDefXvals(gridID, lons);
gridDefYvals(gridID, lats);
   ...
@EndSource
Uwe Schulzweida's avatar
Uwe Schulzweida committed
528
529
@EndFunction
*/
530
int gridCreate(int gridtype, size_t size)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
531
{
532
  if ( CDI_Debug ) Message("gridtype=%s  size=%zu", gridNamePtr(gridtype), size);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
533

534
  gridInit();
535

Uwe Schulzweida's avatar
Uwe Schulzweida committed
536
  grid_t *gridptr = gridNewEntry(CDI_UNDEFID);
537
  if ( ! gridptr ) Error("No memory");
Uwe Schulzweida's avatar
Uwe Schulzweida committed
538

Uwe Schulzweida's avatar
Uwe Schulzweida committed
539
  int gridID = gridptr->self;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
540

541
  if ( CDI_Debug ) Message("gridID: %d", gridID);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
542

543
  cdiGridTypeInit(gridptr, gridtype, size);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
544

Uwe Schulzweida's avatar
Uwe Schulzweida committed
545
  return gridID;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
546
547
}

Uwe Schulzweida's avatar
Uwe Schulzweida committed
548
549
static
void gridDestroyKernel( grid_t * gridptr )
550
{
Deike Kleberg's avatar
Deike Kleberg committed
551
  xassert ( gridptr );
552

553
  int id = gridptr->self;
554

555
  grid_free_components(gridptr);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
556
  Free( gridptr );
557

558
  reshRemove ( id, &gridOps );
559
}
Uwe Schulzweida's avatar
Uwe Schulzweida committed
560

561
562
563
564
565
566
/*
@Function  gridDestroy
@Title     Destroy a horizontal Grid

@Prototype void gridDestroy(int gridID)
@Parameter
Deike Kleberg's avatar
Deike Kleberg committed
567
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate}.
568
569
570
571
572

@EndFunction
*/
void gridDestroy(int gridID)
{
573
  grid_t *gridptr = grid_to_pointer(gridID);
574
  gridptr->vtable->destroy(gridptr);
575
576
}

Uwe Schulzweida's avatar
Uwe Schulzweida committed
577
578
static
void gridDestroyP(void * gridptr)
579
{
580
  ((grid_t *)gridptr)->vtable->destroy((grid_t *)gridptr);
581
582
583
}


584
const char *gridNamePtr(int gridtype)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
585
{
586
  int size = (int) (sizeof(Grids)/sizeof(Grids[0]));
Uwe Schulzweida's avatar
Uwe Schulzweida committed
587

Uwe Schulzweida's avatar
Uwe Schulzweida committed
588
  const char *name = (gridtype >= 0 && gridtype < size) ? Grids[gridtype] : Grids[GRID_GENERIC];
Uwe Schulzweida's avatar
Uwe Schulzweida committed
589

Uwe Schulzweida's avatar
Uwe Schulzweida committed
590
  return name;
Uwe Schulzweida's avatar
Uwe Schulzweida committed
591
592
593
594
595
596
597
598
}


void gridName(int gridtype, char *gridname)
{
  strcpy(gridname, gridNamePtr(gridtype));
}

599
static
600
void *grid_key_to_ptr(grid_t *gridptr, int key)
601
{
602
  void *keyptr = NULL;
603
604
605

  switch (key)
    {
606
607
608
609
610
611
612
613
614
    case CDI_KEY_XNAME:      keyptr = (void*)gridptr->x.name; break;
    case CDI_KEY_XLONGNAME:  keyptr = (void*)gridptr->x.longname; break;
    case CDI_KEY_XUNITS:     keyptr = (void*)gridptr->x.units; break;
    case CDI_KEY_YNAME:      keyptr = (void*)gridptr->y.name; break;
    case CDI_KEY_YLONGNAME:  keyptr = (void*)gridptr->y.longname; break;
    case CDI_KEY_YUNITS:     keyptr = (void*)gridptr->y.units; break;
    case CDI_KEY_XDIMNAME:   keyptr = (void*)gridptr->x.dimname; break;
    case CDI_KEY_YDIMNAME:   keyptr = (void*)gridptr->y.dimname; break;
    case CDI_KEY_VDIMNAME:   keyptr = (void*)gridptr->vdimname; break;
615
616
    case CDI_KEY_MAPPING:    keyptr = (void*)gridptr->mapname; break;
    case CDI_KEY_MAPNAME:    keyptr = (void*)gridptr->mapping; break;
617
618
    }

619
  return keyptr;
620
621
}

622
/*
623
@Function  cdiGridDefKeyStr
624
625
@Title     Define a CDI grid string value from a key

626
@Prototype int cdiGridDefKeyStr(int gridID, int key, int size, const char *mesg)
627
628
629
630
631
632
633
@Parameter
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate}.
    @Item  key      The key to be searched
    @Item  size     The allocated length of the string on input
    @Item  mesg     The address of a string where the data will be read

@Description
634
The function @func{cdiGridDefKeyStr} defines a CDI grid string value from a key.
635
636

@Result
637
@func{cdiGridDefKeyStr} returns 0 if OK and integer value on error.
638
639
640

@EndFunction
*/
641
int cdiGridDefKeyStr(int gridID, int key, int size, const char *mesg)
642
{
643
  if ( size < 1 || mesg == NULL || *mesg == 0 ) return -1;
644

645
  grid_t *gridptr = grid_to_pointer(gridID);
646

647
648
  char *keyptr = (char*)grid_key_to_ptr(gridptr, key);
  if ( keyptr == NULL )
649
650
651
652
653
    {
      Warning("CDI grid string key %d not supported!", key);
      return -1;
    }

654
  gridSetString(keyptr, mesg, (size_t)size);
655
656
657
658
659
660
  gridMark4Update(gridID);

  return 0;
}

/*
661
@Function  cdiGridInqKeyStr
662
663
@Title     Get a CDI grid string value from a key

664
@Prototype int cdiGridInqKeyStr(int gridID, int key, int size, char *mesg)
665
666
667
668
669
670
671
672
673
674
@Parameter
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate}.
    @Item  key      The key to be searched.
    @Item  size     The allocated length of the string on input.
    @Item  mesg     The address of a string where the data will be retrieved.
                    The caller must allocate space for the returned string.
                    The maximum possible length, in characters, of the string
                    is given by the predefined constant @func{CDI_MAX_NAME}.

@Description
675
The function @func{cdiGridInqKeyStr} return a CDI grid string value from a key.
676
677

@Result
678
@func{cdiGridInqKeyStr} returns 0 if OK and integer value on error.
679
680
681

@EndFunction
*/
682
int cdiGridInqKeyStr(int gridID, int key, int size, char *mesg)
683
{
684
  if ( size < 1 || mesg == NULL ) return -1;
685

686
  grid_t *gridptr = grid_to_pointer(gridID);
687
688
  const char *keyptr = (const char*)grid_key_to_ptr(gridptr, key);
  if ( keyptr == NULL)
689
690
691
692
693
    {
      Warning("CDI grid string key %d not supported!", key);
      return -1;
    }

694
  gridGetString(mesg, keyptr, (size_t)size);
695
696
697
698

  return 0;
}

Uwe Schulzweida's avatar
Uwe Schulzweida committed
699
700
701
702
703
704
/*
@Function  gridDefXname
@Title     Define the name of a X-axis

@Prototype void gridDefXname(int gridID, const char *name)
@Parameter
Deike Kleberg's avatar
Deike Kleberg committed
705
706
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate}.
    @Item  name     Name of the X-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
707
708

@Description
709
The function @func{gridDefXname} defines the name of a X-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
710
711
712

@EndFunction
*/
Uwe Schulzweida's avatar
Uwe Schulzweida committed
713
void gridDefXname(int gridID, const char *xname)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
714
{
715
  (void)cdiGridDefKeyStr(gridID, CDI_KEY_XNAME, CDI_MAX_NAME, xname);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
716
717
718
719
720
721
722
723
}

/*
@Function  gridDefXlongname
@Title     Define the longname of a X-axis

@Prototype void gridDefXlongname(int gridID, const char *longname)
@Parameter
Deike Kleberg's avatar
Deike Kleberg committed
724
725
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate}.
    @Item  longname Longname of the X-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
726
727

@Description
728
The function @func{gridDefXlongname} defines the longname of a X-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
729
730
731

@EndFunction
*/
Uwe Schulzweida's avatar
Uwe Schulzweida committed
732
void gridDefXlongname(int gridID, const char *xlongname)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
733
{
734
  (void)cdiGridDefKeyStr(gridID, CDI_KEY_XLONGNAME, CDI_MAX_NAME, xlongname);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
735
736
737
738
739
740
741
742
}

/*
@Function  gridDefXunits
@Title     Define the units of a X-axis

@Prototype void gridDefXunits(int gridID, const char *units)
@Parameter
Deike Kleberg's avatar
Deike Kleberg committed
743
744
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate}.
    @Item  units    Units of the X-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
745
746

@Description
747
The function @func{gridDefXunits} defines the units of a X-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
748
749
750

@EndFunction
*/
Uwe Schulzweida's avatar
Uwe Schulzweida committed
751
void gridDefXunits(int gridID, const char *xunits)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
752
{
753
  (void)cdiGridDefKeyStr(gridID, CDI_KEY_XUNITS, CDI_MAX_NAME, xunits);
754
755
}

Uwe Schulzweida's avatar
Uwe Schulzweida committed
756
757
758
759
760
761
/*
@Function  gridDefYname
@Title     Define the name of a Y-axis

@Prototype void gridDefYname(int gridID, const char *name)
@Parameter
Deike Kleberg's avatar
Deike Kleberg committed
762
763
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate}.
    @Item  name     Name of the Y-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
764
765

@Description
766
The function @func{gridDefYname} defines the name of a Y-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
767
768
769

@EndFunction
*/
Uwe Schulzweida's avatar
Uwe Schulzweida committed
770
void gridDefYname(int gridID, const char *yname)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
771
{
772
  (void)cdiGridDefKeyStr(gridID, CDI_KEY_YNAME, CDI_MAX_NAME, yname);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
773
774
775
776
777
778
779
780
}

/*
@Function  gridDefYlongname
@Title     Define the longname of a Y-axis

@Prototype void gridDefYlongname(int gridID, const char *longname)
@Parameter
Deike Kleberg's avatar
Deike Kleberg committed
781
782
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate}.
    @Item  longname Longname of the Y-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
783
784

@Description
785
The function @func{gridDefYlongname} defines the longname of a Y-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
786
787
788

@EndFunction
*/
Uwe Schulzweida's avatar
Uwe Schulzweida committed
789
void gridDefYlongname(int gridID, const char *ylongname)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
790
{
791
  (void)cdiGridDefKeyStr(gridID, CDI_KEY_YLONGNAME, CDI_MAX_NAME, ylongname);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
792
793
794
795
796
797
798
799
}

/*
@Function  gridDefYunits
@Title     Define the units of a Y-axis

@Prototype void gridDefYunits(int gridID, const char *units)
@Parameter
Deike Kleberg's avatar
Deike Kleberg committed
800
801
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate}.
    @Item  units    Units of the Y-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
802
803

@Description
804
The function @func{gridDefYunits} defines the units of a Y-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
805
806
807

@EndFunction
*/
Uwe Schulzweida's avatar
Uwe Schulzweida committed
808
void gridDefYunits(int gridID, const char *yunits)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
809
{
810
  (void)cdiGridDefKeyStr(gridID, CDI_KEY_YUNITS, CDI_MAX_NAME, yunits);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
811
812
813
814
815
816
}

/*
@Function  gridInqXname
@Title     Get the name of a X-axis

Deike Kleberg's avatar
Deike Kleberg committed
817
@Prototype void gridInqXname(int gridID, char *name)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
818
@Parameter
Uwe Schulzweida's avatar
Uwe Schulzweida committed
819
820
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate} or @fref{vlistInqVarGrid}.
    @Item  name     Name of the X-axis. The caller must allocate space for the
Deike Kleberg's avatar
Deike Kleberg committed
821
                    returned string. The maximum possible length, in characters, of
822
                    the string is given by the predefined constant @func{CDI_MAX_NAME}.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
823
824

@Description
825
The function @func{gridInqXname} returns the name of a X-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
826
827

@Result
828
@func{gridInqXname} returns the name of the X-axis to the parameter name.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
829
830
831
832
833

@EndFunction
*/
void gridInqXname(int gridID, char *xname)
{
834
  (void)cdiGridInqKeyStr(gridID, CDI_KEY_XNAME, CDI_MAX_NAME, xname);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
835
836
837
838
839
840
}

/*
@Function  gridInqXlongname
@Title     Get the longname of a X-axis

Deike Kleberg's avatar
Deike Kleberg committed
841
@Prototype void gridInqXlongname(int gridID, char *longname)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
842
@Parameter
Uwe Schulzweida's avatar
Uwe Schulzweida committed
843
844
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate} or @fref{vlistInqVarGrid}.
    @Item  longname Longname of the X-axis. The caller must allocate space for the
Deike Kleberg's avatar
Deike Kleberg committed
845
                    returned string. The maximum possible length, in characters, of
846
                    the string is given by the predefined constant @func{CDI_MAX_NAME}.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
847
848

@Description
849
The function @func{gridInqXlongname} returns the longname of a X-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
850
851

@Result
852
@func{gridInqXlongname} returns the longname of the X-axis to the parameter longname.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
853
854
855
856
857

@EndFunction
*/
void gridInqXlongname(int gridID, char *xlongname)
{
858
  (void)cdiGridInqKeyStr(gridID, CDI_KEY_XLONGNAME, CDI_MAX_NAME, xlongname);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
859
860
861
862
863
864
}

/*
@Function  gridInqXunits
@Title     Get the units of a X-axis

Deike Kleberg's avatar
Deike Kleberg committed
865
@Prototype void gridInqXunits(int gridID, char *units)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
866
@Parameter
Uwe Schulzweida's avatar
Uwe Schulzweida committed
867
868
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate} or @fref{vlistInqVarGrid}.
    @Item  units    Units of the X-axis. The caller must allocate space for the
Deike Kleberg's avatar
Deike Kleberg committed
869
                    returned string. The maximum possible length, in characters, of
870
                    the string is given by the predefined constant @func{CDI_MAX_NAME}.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
871
872

@Description
873
The function @func{gridInqXunits} returns the units of a X-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
874
875

@Result
876
@func{gridInqXunits} returns the units of the X-axis to the parameter units.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
877
878
879
880
881

@EndFunction
*/
void gridInqXunits(int gridID, char *xunits)
{
882
  (void)cdiGridInqKeyStr(gridID, CDI_KEY_XUNITS, CDI_MAX_NAME, xunits);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
883
884
885
886
887
}


void gridInqXstdname(int gridID, char *xstdname)
{
Uwe Schulzweida's avatar
Uwe Schulzweida committed
888
889
890
891
892
893
  if ( xstdname )
    {
      xstdname[0] = 0;
      grid_t *gridptr = grid_to_pointer(gridID);
      if ( gridptr->x.stdname ) strcpy(xstdname, gridptr->x.stdname);
    }
Uwe Schulzweida's avatar
Uwe Schulzweida committed
894
895
896
897
898
899
}

/*
@Function  gridInqYname
@Title     Get the name of a Y-axis

Deike Kleberg's avatar
Deike Kleberg committed
900
@Prototype void gridInqYname(int gridID, char *name)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
901
@Parameter
Uwe Schulzweida's avatar
Uwe Schulzweida committed
902
903
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate} or @fref{vlistInqVarGrid}.
    @Item  name     Name of the Y-axis. The caller must allocate space for the
Deike Kleberg's avatar
Deike Kleberg committed
904
                    returned string. The maximum possible length, in characters, of
905
                    the string is given by the predefined constant @func{CDI_MAX_NAME}.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
906
907

@Description
908
The function @func{gridInqYname} returns the name of a Y-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
909
910

@Result
911
@func{gridInqYname} returns the name of the Y-axis to the parameter name.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
912
913
914
915
916

@EndFunction
*/
void gridInqYname(int gridID, char *yname)
{
917
  (void)cdiGridInqKeyStr(gridID, CDI_KEY_YNAME, CDI_MAX_NAME, yname);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
918
919
920
921
922
923
}

/*
@Function  gridInqYlongname
@Title     Get the longname of a Y-axis

924
@Prototype void gridInqYlongname(int gridID, char *longname)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
925
@Parameter
Uwe Schulzweida's avatar
Uwe Schulzweida committed
926
927
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate} or @fref{vlistInqVarGrid}.
    @Item  longname Longname of the Y-axis. The caller must allocate space for the
Deike Kleberg's avatar
Deike Kleberg committed
928
                    returned string. The maximum possible length, in characters, of
929
                    the string is given by the predefined constant @func{CDI_MAX_NAME}.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
930
931

@Description
932
The function @func{gridInqYlongname} returns the longname of a Y-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
933
934

@Result
935
@func{gridInqYlongname} returns the longname of the Y-axis to the parameter longname.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
936
937
938
939
940

@EndFunction
*/
void gridInqYlongname(int gridID, char *ylongname)
{
941
  (void)cdiGridInqKeyStr(gridID, CDI_KEY_YLONGNAME, CDI_MAX_NAME, ylongname);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
942
943
944
945
946
947
}

/*
@Function  gridInqYunits
@Title     Get the units of a Y-axis

Deike Kleberg's avatar
Deike Kleberg committed
948
@Prototype void gridInqYunits(int gridID, char *units)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
949
@Parameter
Uwe Schulzweida's avatar
Uwe Schulzweida committed
950
951
    @Item  gridID   Grid ID, from a previous call to @fref{gridCreate} or @fref{vlistInqVarGrid}.
    @Item  units    Units of the Y-axis. The caller must allocate space for the
Deike Kleberg's avatar
Deike Kleberg committed
952
                    returned string. The maximum possible length, in characters, of
953
                    the string is given by the predefined constant @func{CDI_MAX_NAME}.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
954
955

@Description
956
The function @func{gridInqYunits} returns the units of a Y-axis.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
957
958

@Result
959
@func{gridInqYunits} returns the units of the Y-axis to the parameter units.
Uwe Schulzweida's avatar
Uwe Schulzweida committed
960
961
962
963
964

@EndFunction
*/
void gridInqYunits(int gridID, char *yunits)
{
965
  (void)cdiGridInqKeyStr(gridID, CDI_KEY_YUNITS, CDI_MAX_NAME, yunits);
Uwe Schulzweida's avatar
Uwe Schulzweida committed
966
967
}

968

Uwe Schulzweida's avatar
Uwe Schulzweida committed
969
970
void gridInqYstdname(int gridID, char *ystdname)
{
Uwe Schulzweida's avatar
Uwe Schulzweida committed
971
972
973
974
975
976
  if ( ystdname )
    {
      ystdname[0] = 0;
      grid_t *gridptr = grid_to_pointer(gridID);
      if ( gridptr->y.stdname ) strcpy(ystdname, gridptr->y.stdname);
    }
Uwe Schulzweida's avatar
Uwe Schulzweida committed
977
978
}

979

980
void gridDefProj(int gridID, int projID)
Uwe Schulzweida's avatar
Uwe Schulzweida committed
981
{
982
  grid_t *gridptr = grid_to_pointer(gridID);
983
  gridptr->proj = projID;
984