1
/******************************************************************************
2
* $Id: pj_gridinfo.c,v 1.6 2004/10/30 04:03:03 fwarmerdam Exp $
5
* Purpose: Functions for handling individual PJ_GRIDINFO's. Includes
6
* loaders for all formats but CTABLE (in nad_init.c).
7
* Author: Frank Warmerdam, warmerdam@pobox.com
9
******************************************************************************
10
* Copyright (c) 2000, Frank Warmerdam <warmerdam@pobox.com>
12
* Permission is hereby granted, free of charge, to any person obtaining a
13
* copy of this software and associated documentation files (the "Software"),
14
* to deal in the Software without restriction, including without limitation
15
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
16
* and/or sell copies of the Software, and to permit persons to whom the
17
* Software is furnished to do so, subject to the following conditions:
19
* The above copyright notice and this permission notice shall be included
20
* in all copies or substantial portions of the Software.
22
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28
* DEALINGS IN THE SOFTWARE.
29
******************************************************************************
31
* $Log: pj_gridinfo.c,v $
32
* Revision 1.6 2004/10/30 04:03:03 fwarmerdam
33
* fixed reported information in ctable debug message
35
* Revision 1.5 2003/08/20 13:23:58 warmerda
36
* Avoid unsigned char / char casting issues for VC++.
38
* Revision 1.4 2003/03/19 03:36:41 warmerda
39
* Fixed so swap_words() works when it should.
41
* Revision 1.3 2003/03/17 19:44:45 warmerda
42
* improved debugging, reduce header read size
44
* Revision 1.2 2003/03/17 18:56:34 warmerda
45
* implement heirarchical NTv2 gridinfos
47
* Revision 1.1 2003/03/15 06:01:18 warmerda
60
/************************************************************************/
63
/* Convert the byte order of the given word(s) in place. */
64
/************************************************************************/
66
static int byte_order_test = 1;
67
#define IS_LSB (((unsigned char *) (&byte_order_test))[0] == 1)
69
static void swap_words( unsigned char *data, int word_size, int word_count )
74
for( word = 0; word < word_count; word++ )
78
for( i = 0; i < word_size/2; i++ )
83
data[i] = data[word_size-i-1];
84
data[word_size-i-1] = t;
91
/************************************************************************/
92
/* pj_gridinfo_free() */
93
/************************************************************************/
95
void pj_gridinfo_free( PJ_GRIDINFO *gi )
101
if( gi->child != NULL )
103
PJ_GRIDINFO *child, *next;
105
for( child = gi->child; child != NULL; child=next)
108
pj_gridinfo_free( child );
115
free( gi->gridname );
116
if( gi->filename != NULL )
117
free( gi->filename );
122
/************************************************************************/
123
/* pj_gridinfo_load() */
125
/* This function is intended to implement delayed loading of */
126
/* the data contents of a grid file. The header and related */
127
/* stuff are loaded by pj_gridinfo_init(). */
128
/************************************************************************/
130
int pj_gridinfo_load( PJ_GRIDINFO *gi )
133
if( gi == NULL || gi->ct == NULL )
136
/* -------------------------------------------------------------------- */
137
/* ctable is currently loaded on initialization though there is */
138
/* no real reason not to support delayed loading for it as well. */
139
/* -------------------------------------------------------------------- */
140
if( strcmp(gi->format,"ctable") == 0 )
145
fid = pj_open_lib( gi->filename, "rb" );
153
result = nad_ctable_load( gi->ct, fid );
160
/* -------------------------------------------------------------------- */
162
/* We process one line at a time. Note that the array storage */
163
/* direction (e-w) is different in the NTv1 file and what */
164
/* the CTABLE is supposed to have. The phi/lam are also */
165
/* reversed, and we have to be aware of byte swapping. */
166
/* -------------------------------------------------------------------- */
167
else if( strcmp(gi->format,"ntv1") == 0 )
173
fid = pj_open_lib( gi->filename, "rb" );
181
fseek( fid, gi->grid_offset, SEEK_SET );
183
row_buf = (double *) pj_malloc(gi->ct->lim.lam * sizeof(double) * 2);
184
gi->ct->cvs = (FLP *) pj_malloc(gi->ct->lim.lam*gi->ct->lim.phi*sizeof(FLP));
185
if( row_buf == NULL || gi->ct->cvs == NULL )
191
for( row = 0; row < gi->ct->lim.phi; row++ )
195
double *diff_seconds;
197
if( fread( row_buf, sizeof(double), gi->ct->lim.lam * 2, fid )
198
!= 2 * gi->ct->lim.lam )
200
pj_dalloc( row_buf );
201
pj_dalloc( gi->ct->cvs );
207
swap_words( (unsigned char *) row_buf, 8, gi->ct->lim.lam*2 );
209
/* convert seconds to radians */
210
diff_seconds = row_buf;
212
for( i = 0; i < gi->ct->lim.lam; i++ )
214
cvs = gi->ct->cvs + (row) * gi->ct->lim.lam
215
+ (gi->ct->lim.lam - i - 1);
217
cvs->phi = *(diff_seconds++) * ((PI/180.0) / 3600.0);
218
cvs->lam = *(diff_seconds++) * ((PI/180.0) / 3600.0);
222
pj_dalloc( row_buf );
229
/* -------------------------------------------------------------------- */
231
/* We process one line at a time. Note that the array storage */
232
/* direction (e-w) is different in the NTv2 file and what */
233
/* the CTABLE is supposed to have. The phi/lam are also */
234
/* reversed, and we have to be aware of byte swapping. */
235
/* -------------------------------------------------------------------- */
236
else if( strcmp(gi->format,"ntv2") == 0 )
242
if( getenv("PROJ_DEBUG") != NULL )
244
fprintf( stderr, "NTv2 - loading grid %s\n", gi->ct->id );
247
fid = pj_open_lib( gi->filename, "rb" );
255
fseek( fid, gi->grid_offset, SEEK_SET );
257
row_buf = (float *) pj_malloc(gi->ct->lim.lam * sizeof(float) * 4);
258
gi->ct->cvs = (FLP *) pj_malloc(gi->ct->lim.lam*gi->ct->lim.phi*sizeof(FLP));
259
if( row_buf == NULL || gi->ct->cvs == NULL )
265
for( row = 0; row < gi->ct->lim.phi; row++ )
271
if( fread( row_buf, sizeof(float), gi->ct->lim.lam*4, fid )
272
!= 4 * gi->ct->lim.lam )
274
pj_dalloc( row_buf );
275
pj_dalloc( gi->ct->cvs );
282
swap_words( (unsigned char *) row_buf, 4,
285
/* convert seconds to radians */
286
diff_seconds = row_buf;
288
for( i = 0; i < gi->ct->lim.lam; i++ )
290
cvs = gi->ct->cvs + (row) * gi->ct->lim.lam
291
+ (gi->ct->lim.lam - i - 1);
293
cvs->phi = *(diff_seconds++) * ((PI/180.0) / 3600.0);
294
cvs->lam = *(diff_seconds++) * ((PI/180.0) / 3600.0);
295
diff_seconds += 2; /* skip accuracy values */
299
pj_dalloc( row_buf );
312
/************************************************************************/
313
/* pj_gridinfo_init_ntv2() */
315
/* Load a ntv2 (.gsb) file. */
316
/************************************************************************/
318
static int pj_gridinfo_init_ntv2( FILE *fid, PJ_GRIDINFO *gilist )
321
unsigned char header[11*16];
322
int num_subfiles, subfile;
324
assert( sizeof(int) == 4 );
325
assert( sizeof(double) == 8 );
326
if( sizeof(int) != 4 || sizeof(double) != 8 )
329
"basic types of inappropraiate size in pj_gridinfo_init_ntv2()\n" );
334
/* -------------------------------------------------------------------- */
335
/* Read the overview header. */
336
/* -------------------------------------------------------------------- */
337
if( fread( header, sizeof(header), 1, fid ) != 1 )
343
/* -------------------------------------------------------------------- */
344
/* Byte swap interesting fields if needed. */
345
/* -------------------------------------------------------------------- */
348
swap_words( header+8, 4, 1 );
349
swap_words( header+8+16, 4, 1 );
350
swap_words( header+8+32, 4, 1 );
351
swap_words( header+8+7*16, 8, 1 );
352
swap_words( header+8+8*16, 8, 1 );
353
swap_words( header+8+9*16, 8, 1 );
354
swap_words( header+8+10*16, 8, 1 );
357
/* -------------------------------------------------------------------- */
358
/* Get the subfile count out ... all we really use for now. */
359
/* -------------------------------------------------------------------- */
360
memcpy( &num_subfiles, header+8+32, 4 );
362
/* ==================================================================== */
363
/* Step through the subfiles, creating a PJ_GRIDINFO for each. */
364
/* ==================================================================== */
365
for( subfile = 0; subfile < num_subfiles; subfile++ )
372
/* -------------------------------------------------------------------- */
374
/* -------------------------------------------------------------------- */
375
if( fread( header, sizeof(header), 1, fid ) != 1 )
381
if( strncmp((const char *) header,"SUB_NAME",8) != 0 )
387
/* -------------------------------------------------------------------- */
388
/* Byte swap interesting fields if needed. */
389
/* -------------------------------------------------------------------- */
392
swap_words( header+8+16*4, 8, 1 );
393
swap_words( header+8+16*5, 8, 1 );
394
swap_words( header+8+16*6, 8, 1 );
395
swap_words( header+8+16*7, 8, 1 );
396
swap_words( header+8+16*8, 8, 1 );
397
swap_words( header+8+16*9, 8, 1 );
398
swap_words( header+8+16*10, 4, 1 );
401
/* -------------------------------------------------------------------- */
402
/* Initialize a corresponding "ct" structure. */
403
/* -------------------------------------------------------------------- */
404
ct = (struct CTABLE *) pj_malloc(sizeof(struct CTABLE));
405
strncpy( ct->id, (const char *) header + 8, 8 );
408
ct->ll.lam = - *((double *) (header+7*16+8)); /* W_LONG */
409
ct->ll.phi = *((double *) (header+4*16+8)); /* S_LAT */
411
ur.lam = - *((double *) (header+6*16+8)); /* E_LONG */
412
ur.phi = *((double *) (header+5*16+8)); /* N_LAT */
414
ct->del.lam = *((double *) (header+9*16+8));
415
ct->del.phi = *((double *) (header+8*16+8));
417
ct->lim.lam = (int) (fabs(ur.lam-ct->ll.lam)/ct->del.lam + 0.5) + 1;
418
ct->lim.phi = (int) (fabs(ur.phi-ct->ll.phi)/ct->del.phi + 0.5) + 1;
420
if( getenv("PROJ_DEBUG") != NULL )
422
"NTv2 %s %dx%d: LL=(%.9g,%.9g) UR=(%.9g,%.9g)\n",
424
ct->lim.lam, ct->lim.phi,
425
ct->ll.lam/3600.0, ct->ll.phi/3600.0,
426
ur.lam/3600.0, ur.phi/3600.0 );
428
ct->ll.lam *= DEG_TO_RAD/3600.0;
429
ct->ll.phi *= DEG_TO_RAD/3600.0;
430
ct->del.lam *= DEG_TO_RAD/3600.0;
431
ct->del.phi *= DEG_TO_RAD/3600.0;
433
memcpy( &gs_count, header + 8 + 16*10, 4 );
434
if( gs_count != ct->lim.lam * ct->lim.phi )
437
"GS_COUNT(%d) does not match expected cells (%dx%d=%d)\n",
438
gs_count, ct->lim.lam, ct->lim.phi,
439
ct->lim.lam * ct->lim.phi );
446
/* -------------------------------------------------------------------- */
447
/* Create a new gridinfo for this if we aren't processing the */
448
/* 1st subfile, and initialize our grid info. */
449
/* -------------------------------------------------------------------- */
454
gi = (PJ_GRIDINFO *) pj_malloc(sizeof(PJ_GRIDINFO));
455
memset( gi, 0, sizeof(PJ_GRIDINFO) );
457
gi->gridname = strdup( gilist->gridname );
458
gi->filename = strdup( gilist->filename );
464
gi->grid_offset = ftell( fid );
466
/* -------------------------------------------------------------------- */
467
/* Attach to the correct list or sublist. */
468
/* -------------------------------------------------------------------- */
469
if( strncmp((const char *)header+24,"NONE",4) == 0 )
475
for( lnk = gilist; lnk->next != NULL; lnk = lnk->next ) {}
483
PJ_GRIDINFO *gp = gilist;
486
&& strncmp(gp->ct->id,(const char*)header+24,8) != 0 )
491
if( getenv("PROJ_DEBUG") != NULL )
492
fprintf( stderr, "pj_gridinfo_init_ntv2(): "
493
"failed to find parent %8.8s for %.\n",
494
(const char *) header+24, gi->ct->id );
496
for( lnk = gp; lnk->next != NULL; lnk = lnk->next ) {}
499
else if( gp->child == NULL )
505
for( lnk = gp->child; lnk->next != NULL; lnk = lnk->next ) {}
510
/* -------------------------------------------------------------------- */
511
/* Seek past the data. */
512
/* -------------------------------------------------------------------- */
513
fseek( fid, gs_count * 16, SEEK_CUR );
519
/************************************************************************/
520
/* pj_gridinfo_init_ntv1() */
522
/* Load an NTv1 style Canadian grid shift file. */
523
/************************************************************************/
525
static int pj_gridinfo_init_ntv1( FILE * fid, PJ_GRIDINFO *gi )
528
unsigned char header[176];
532
assert( sizeof(int) == 4 );
533
assert( sizeof(double) == 8 );
534
if( sizeof(int) != 4 || sizeof(double) != 8 )
537
"basic types of inappropraiate size in nad_load_ntv1()\n" );
542
/* -------------------------------------------------------------------- */
543
/* Read the header. */
544
/* -------------------------------------------------------------------- */
545
if( fread( header, sizeof(header), 1, fid ) != 1 )
551
/* -------------------------------------------------------------------- */
552
/* Regularize fields of interest. */
553
/* -------------------------------------------------------------------- */
556
swap_words( header+8, 4, 1 );
557
swap_words( header+24, 8, 1 );
558
swap_words( header+40, 8, 1 );
559
swap_words( header+56, 8, 1 );
560
swap_words( header+72, 8, 1 );
561
swap_words( header+88, 8, 1 );
562
swap_words( header+104, 8, 1 );
565
if( *((int *) (header+8)) != 12 )
568
printf("NTv1 grid shift file has wrong record count, corrupt?\n");
572
/* -------------------------------------------------------------------- */
573
/* Fill in CTABLE structure. */
574
/* -------------------------------------------------------------------- */
575
ct = (struct CTABLE *) pj_malloc(sizeof(struct CTABLE));
576
strcpy( ct->id, "NTv1 Grid Shift File" );
578
ct->ll.lam = - *((double *) (header+72));
579
ct->ll.phi = *((double *) (header+24));
580
ur.lam = - *((double *) (header+56));
581
ur.phi = *((double *) (header+40));
582
ct->del.lam = *((double *) (header+104));
583
ct->del.phi = *((double *) (header+88));
584
ct->lim.lam = (int) (fabs(ur.lam-ct->ll.lam)/ct->del.lam + 0.5) + 1;
585
ct->lim.phi = (int) (fabs(ur.phi-ct->ll.phi)/ct->del.phi + 0.5) + 1;
587
if( getenv("PROJ_DEBUG") != NULL )
589
"NTv1 %dx%d: LL=(%.9g,%.9g) UR=(%.9g,%.9g)\n",
590
ct->lim.lam, ct->lim.phi,
591
ct->ll.lam, ct->ll.phi, ur.lam, ur.phi );
593
ct->ll.lam *= DEG_TO_RAD;
594
ct->ll.phi *= DEG_TO_RAD;
595
ct->del.lam *= DEG_TO_RAD;
596
ct->del.phi *= DEG_TO_RAD;
600
gi->grid_offset = ftell( fid );
606
/************************************************************************/
607
/* pj_gridinfo_init() */
609
/* Open and parse header details from a datum gridshift file */
610
/* returning a list of PJ_GRIDINFOs for the grids in that */
611
/* file. This superceeds use of nad_init() for modern */
613
/************************************************************************/
615
PJ_GRIDINFO *pj_gridinfo_init( const char *gridname )
618
char fname[MAX_PATH_FILENAME+1];
623
errno = pj_errno = 0;
625
/* -------------------------------------------------------------------- */
626
/* Initialize a GRIDINFO with stub info we would use if it */
627
/* cannot be loaded. */
628
/* -------------------------------------------------------------------- */
629
gilist = (PJ_GRIDINFO *) pj_malloc(sizeof(PJ_GRIDINFO));
630
memset( gilist, 0, sizeof(PJ_GRIDINFO) );
632
gilist->gridname = strdup( gridname );
633
gilist->filename = NULL;
634
gilist->format = "missing";
635
gilist->grid_offset = 0;
639
/* -------------------------------------------------------------------- */
640
/* Open the file using the usual search rules. */
641
/* -------------------------------------------------------------------- */
642
strcpy(fname, gridname);
643
if (!(fp = pj_open_lib(fname, "rb"))) {
648
gilist->filename = strdup(fname);
650
/* -------------------------------------------------------------------- */
651
/* Load a header, to determine the file type. */
652
/* -------------------------------------------------------------------- */
653
if( fread( header, sizeof(header), 1, fp ) != 1 )
660
fseek( fp, SEEK_SET, 0 );
662
/* -------------------------------------------------------------------- */
663
/* Determine file type. */
664
/* -------------------------------------------------------------------- */
665
if( strncmp(header + 0, "HEADER", 6) == 0
666
&& strncmp(header + 96, "W GRID", 6) == 0
667
&& strncmp(header + 144, "TO NAD83 ", 16) == 0 )
669
pj_gridinfo_init_ntv1( fp, gilist );
672
else if( strncmp(header + 0, "NUM_OREC", 8) == 0
673
&& strncmp(header + 48, "GS_TYPE", 7) == 0 )
675
pj_gridinfo_init_ntv2( fp, gilist );
680
struct CTABLE *ct = nad_ctable_init( fp );
682
gilist->format = "ctable";
685
if( getenv("PROJ_DEBUG") != NULL )
687
"Ctable %s %dx%d: LL=(%.9g,%.9g) UR=(%.9g,%.9g)\n",
689
ct->lim.lam, ct->lim.phi,
690
ct->ll.lam * RAD_TO_DEG, ct->ll.phi * RAD_TO_DEG,
691
(ct->ll.lam + (ct->lim.lam-1)*ct->del.lam) * RAD_TO_DEG,
692
(ct->ll.phi + (ct->lim.phi-1)*ct->del.phi) * RAD_TO_DEG );