1
/******************************************************************************
2
* $Id: ntv2dataset.cpp 21682 2011-02-11 21:25:05Z warmerdam $
4
* Project: Horizontal Datum Formats
5
* Purpose: Implementation of NTv2 datum shift format used in Canada, France,
6
* Australia and elsewhere.
7
* Author: Frank Warmerdam, warmerdam@pobox.com
8
* Financial Support: i-cubed (http://www.i-cubed.com)
10
******************************************************************************
11
* Copyright (c) 2010, Frank Warmerdam
13
* Permission is hereby granted, free of charge, to any person obtaining a
14
* copy of this software and associated documentation files (the "Software"),
15
* to deal in the Software without restriction, including without limitation
16
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
17
* and/or sell copies of the Software, and to permit persons to whom the
18
* Software is furnished to do so, subject to the following conditions:
20
* The above copyright notice and this permission notice shall be included
21
* in all copies or substantial portions of the Software.
23
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29
* DEALINGS IN THE SOFTWARE.
30
****************************************************************************/
32
#include "rawdataset.h"
33
#include "cpl_string.h"
34
#include "ogr_srs_api.h"
36
CPL_CVSID("$Id: ntv2dataset.cpp 21682 2011-02-11 21:25:05Z warmerdam $");
39
* The header for the file, and each grid consists of 11 16byte records.
40
* The first half is an ASCII label, and the second half is the value
41
* often in a little endian int or float.
45
00000000 4e 55 4d 5f 4f 52 45 43 0b 00 00 00 00 00 00 00 |NUM_OREC........|
46
00000010 4e 55 4d 5f 53 52 45 43 0b 00 00 00 00 00 00 00 |NUM_SREC........|
47
00000020 4e 55 4d 5f 46 49 4c 45 01 00 00 00 00 00 00 00 |NUM_FILE........|
48
00000030 47 53 5f 54 59 50 45 20 53 45 43 4f 4e 44 53 20 |GS_TYPE SECONDS |
49
00000040 56 45 52 53 49 4f 4e 20 49 47 4e 30 37 5f 30 31 |VERSION IGN07_01|
50
00000050 53 59 53 54 45 4d 5f 46 4e 54 46 20 20 20 20 20 |SYSTEM_FNTF |
51
00000060 53 59 53 54 45 4d 5f 54 52 47 46 39 33 20 20 20 |SYSTEM_TRGF93 |
52
00000070 4d 41 4a 4f 52 5f 46 20 cd cc cc 4c c2 54 58 41 |MAJOR_F ...L.TXA|
53
00000080 4d 49 4e 4f 52 5f 46 20 00 00 00 c0 88 3f 58 41 |MINOR_F .....?XA|
54
00000090 4d 41 4a 4f 52 5f 54 20 00 00 00 40 a6 54 58 41 |MAJOR_T ...@.TXA|
55
000000a0 4d 49 4e 4f 52 5f 54 20 27 e0 1a 14 c4 3f 58 41 |MINOR_T '....?XA|
56
000000b0 53 55 42 5f 4e 41 4d 45 46 52 41 4e 43 45 20 20 |SUB_NAMEFRANCE |
57
000000c0 50 41 52 45 4e 54 20 20 4e 4f 4e 45 20 20 20 20 |PARENT NONE |
58
000000d0 43 52 45 41 54 45 44 20 33 31 2f 31 30 2f 30 37 |CREATED 31/10/07|
59
000000e0 55 50 44 41 54 45 44 20 20 20 20 20 20 20 20 20 |UPDATED |
60
000000f0 53 5f 4c 41 54 20 20 20 00 00 00 00 80 04 02 41 |S_LAT .......A|
61
00000100 4e 5f 4c 41 54 20 20 20 00 00 00 00 00 da 06 41 |N_LAT .......A|
62
00000110 45 5f 4c 4f 4e 47 20 20 00 00 00 00 00 94 e1 c0 |E_LONG ........|
63
00000120 57 5f 4c 4f 4e 47 20 20 00 00 00 00 00 56 d3 40 |W_LONG .....V.@|
64
00000130 4c 41 54 5f 49 4e 43 20 00 00 00 00 00 80 76 40 |LAT_INC ......v@|
65
00000140 4c 4f 4e 47 5f 49 4e 43 00 00 00 00 00 80 76 40 |LONG_INC......v@|
66
00000150 47 53 5f 43 4f 55 4e 54 a4 43 00 00 00 00 00 00 |GS_COUNT.C......|
67
00000160 94 f7 c1 3e 70 ee a3 3f 2a c7 84 3d ff 42 af 3d |...>p..?*..=.B.=|
69
the actual grid data is a raster with 4 float32 bands (lat offset, long
70
offset, lat error, long error). The offset values are in arc seconds.
71
The grid is flipped in the x and y axis from our usual GDAL orientation.
72
That is, the first pixel is the south east corner with scanlines going
73
east to west, and rows from south to north. As a GDAL dataset we represent
74
these both in the more conventional orientation.
77
/************************************************************************/
78
/* ==================================================================== */
80
/* ==================================================================== */
81
/************************************************************************/
83
class NTv2Dataset : public RawDataset
86
VSILFILE *fpImage; // image data file.
89
vsi_l_offset nGridOffset;
91
double adfGeoTransform[6];
93
void CaptureMetadataItem( char *pszItem );
95
int OpenGrid( char *pachGridHeader, vsi_l_offset nDataStart );
101
virtual CPLErr SetGeoTransform( double * padfTransform );
102
virtual CPLErr GetGeoTransform( double * padfTransform );
103
virtual const char *GetProjectionRef();
104
virtual void FlushCache(void);
106
static GDALDataset *Open( GDALOpenInfo * );
107
static int Identify( GDALOpenInfo * );
108
static GDALDataset *Create( const char * pszFilename,
109
int nXSize, int nYSize, int nBands,
110
GDALDataType eType, char ** papszOptions );
113
/************************************************************************/
114
/* ==================================================================== */
116
/* ==================================================================== */
117
/************************************************************************/
119
/************************************************************************/
121
/************************************************************************/
123
NTv2Dataset::NTv2Dataset()
128
/************************************************************************/
130
/************************************************************************/
132
NTv2Dataset::~NTv2Dataset()
137
if( fpImage != NULL )
138
VSIFCloseL( fpImage );
141
/************************************************************************/
143
/************************************************************************/
145
void NTv2Dataset::FlushCache()
148
/* -------------------------------------------------------------------- */
149
/* Nothing to do in readonly mode, or if nothing seems to have */
150
/* changed metadata wise. */
151
/* -------------------------------------------------------------------- */
152
if( eAccess != GA_Update || !(GetPamFlags() & GPF_DIRTY) )
154
RawDataset::FlushCache();
158
/* -------------------------------------------------------------------- */
159
/* Load grid and file headers. */
160
/* -------------------------------------------------------------------- */
161
char achFileHeader[11*16];
162
char achGridHeader[11*16];
164
VSIFSeekL( fpImage, 0, SEEK_SET );
165
VSIFReadL( achFileHeader, 11, 16, fpImage );
167
VSIFSeekL( fpImage, nGridOffset, SEEK_SET );
168
VSIFReadL( achGridHeader, 11, 16, fpImage );
170
/* -------------------------------------------------------------------- */
171
/* Update the grid, and file headers with any available */
172
/* metadata. If all available metadata is recognised then mark */
173
/* things "clean" from a PAM point of view. */
174
/* -------------------------------------------------------------------- */
175
char **papszMD = GetMetadata();
177
int bSomeLeftOver = FALSE;
179
for( i = 0; papszMD != NULL && papszMD[i] != NULL; i++ )
182
const char *pszValue = CPLParseNameValue( papszMD[i], &pszKey );
184
if( EQUAL(pszKey,"GS_TYPE") )
186
memcpy( achFileHeader + 3*16+8, " ", 8 );
187
memcpy( achFileHeader + 3*16+8, pszValue, MIN(8,strlen(pszValue)) );
189
else if( EQUAL(pszKey,"VERSION") )
191
memcpy( achFileHeader + 4*16+8, " ", 8 );
192
memcpy( achFileHeader + 4*16+8, pszValue, MIN(8,strlen(pszValue)) );
194
else if( EQUAL(pszKey,"SYSTEM_F") )
196
memcpy( achFileHeader + 5*16+8, " ", 8 );
197
memcpy( achFileHeader + 5*16+8, pszValue, MIN(8,strlen(pszValue)) );
199
else if( EQUAL(pszKey,"SYSTEM_T") )
201
memcpy( achFileHeader + 6*16+8, " ", 8 );
202
memcpy( achFileHeader + 6*16+8, pszValue, MIN(8,strlen(pszValue)) );
204
else if( EQUAL(pszKey,"MAJOR_F") )
206
double dfValue = atof(pszValue);
207
CPL_LSBPTR64( &dfValue );
208
memcpy( achFileHeader + 7*16+8, &dfValue, 8 );
210
else if( EQUAL(pszKey,"MINOR_F") )
212
double dfValue = atof(pszValue);
213
CPL_LSBPTR64( &dfValue );
214
memcpy( achFileHeader + 8*16+8, &dfValue, 8 );
216
else if( EQUAL(pszKey,"MAJOR_T") )
218
double dfValue = atof(pszValue);
219
CPL_LSBPTR64( &dfValue );
220
memcpy( achFileHeader + 9*16+8, &dfValue, 8 );
222
else if( EQUAL(pszKey,"MINOR_T") )
224
double dfValue = atof(pszValue);
225
CPL_LSBPTR64( &dfValue );
226
memcpy( achFileHeader + 10*16+8, &dfValue, 8 );
228
else if( EQUAL(pszKey,"SUB_NAME") )
230
memcpy( achGridHeader + 0*16+8, " ", 8 );
231
memcpy( achGridHeader + 0*16+8, pszValue, MIN(8,strlen(pszValue)) );
233
else if( EQUAL(pszKey,"PARENT") )
235
memcpy( achGridHeader + 1*16+8, " ", 8 );
236
memcpy( achGridHeader + 1*16+8, pszValue, MIN(8,strlen(pszValue)) );
238
else if( EQUAL(pszKey,"CREATED") )
240
memcpy( achGridHeader + 2*16+8, " ", 8 );
241
memcpy( achGridHeader + 2*16+8, pszValue, MIN(8,strlen(pszValue)) );
243
else if( EQUAL(pszKey,"UPDATED") )
245
memcpy( achGridHeader + 3*16+8, " ", 8 );
246
memcpy( achGridHeader + 3*16+8, pszValue, MIN(8,strlen(pszValue)) );
250
bSomeLeftOver = TRUE;
256
/* -------------------------------------------------------------------- */
257
/* Load grid and file headers. */
258
/* -------------------------------------------------------------------- */
259
VSIFSeekL( fpImage, 0, SEEK_SET );
260
VSIFWriteL( achFileHeader, 11, 16, fpImage );
262
VSIFSeekL( fpImage, nGridOffset, SEEK_SET );
263
VSIFWriteL( achGridHeader, 11, 16, fpImage );
265
/* -------------------------------------------------------------------- */
266
/* Clear flags if we got everything, then let pam and below do */
267
/* their flushing. */
268
/* -------------------------------------------------------------------- */
270
SetPamFlags( GetPamFlags() & (~GPF_DIRTY) );
272
RawDataset::FlushCache();
275
/************************************************************************/
277
/************************************************************************/
279
int NTv2Dataset::Identify( GDALOpenInfo *poOpenInfo )
282
if( EQUALN(poOpenInfo->pszFilename,"NTv2:",5) )
285
if( poOpenInfo->nHeaderBytes < 64 )
288
if( !EQUALN((const char *)poOpenInfo->pabyHeader + 0, "NUM_OREC", 8 ) )
291
if( !EQUALN((const char *)poOpenInfo->pabyHeader +16, "NUM_SREC", 8 ) )
297
/************************************************************************/
299
/************************************************************************/
301
GDALDataset *NTv2Dataset::Open( GDALOpenInfo * poOpenInfo )
304
if( !Identify( poOpenInfo ) )
307
/* -------------------------------------------------------------------- */
308
/* Are we targetting a particular grid? */
309
/* -------------------------------------------------------------------- */
310
CPLString osFilename;
311
int iTargetGrid = -1;
313
if( EQUALN(poOpenInfo->pszFilename,"NTv2:",5) )
315
const char *pszRest = poOpenInfo->pszFilename+5;
317
iTargetGrid = atoi(pszRest);
318
while( *pszRest != '\0' && *pszRest != ':' )
321
if( *pszRest == ':' )
324
osFilename = pszRest;
327
osFilename = poOpenInfo->pszFilename;
329
/* -------------------------------------------------------------------- */
330
/* Create a corresponding GDALDataset. */
331
/* -------------------------------------------------------------------- */
334
poDS = new NTv2Dataset();
335
poDS->eAccess = poOpenInfo->eAccess;
337
/* -------------------------------------------------------------------- */
339
/* -------------------------------------------------------------------- */
340
if( poOpenInfo->eAccess == GA_ReadOnly )
341
poDS->fpImage = VSIFOpenL( osFilename, "rb" );
343
poDS->fpImage = VSIFOpenL( osFilename, "rb+" );
345
if( poDS->fpImage == NULL )
351
/* -------------------------------------------------------------------- */
352
/* Read the file header. */
353
/* -------------------------------------------------------------------- */
354
char achHeader[11*16];
355
GInt32 nSubFileCount;
359
VSIFSeekL( poDS->fpImage, 0, SEEK_SET );
360
VSIFReadL( achHeader, 11, 16, poDS->fpImage );
362
CPL_LSBPTR32( achHeader + 2*16 + 8 );
363
memcpy( &nSubFileCount, achHeader + 2*16 + 8, 4 );
364
if (nSubFileCount <= 0 || nSubFileCount >= 1024)
366
CPLError(CE_Failure, CPLE_AppDefined,
367
"Invalid value for NUM_FILE : %d", nSubFileCount);
372
poDS->CaptureMetadataItem( achHeader + 3*16 );
373
poDS->CaptureMetadataItem( achHeader + 4*16 );
374
poDS->CaptureMetadataItem( achHeader + 5*16 );
375
poDS->CaptureMetadataItem( achHeader + 6*16 );
377
memcpy( &dfValue, achHeader + 7*16 + 8, 8 );
378
CPL_LSBPTR64( &dfValue );
379
osFValue.Printf( "%.15g", dfValue );
380
poDS->SetMetadataItem( "MAJOR_F", osFValue );
382
memcpy( &dfValue, achHeader + 8*16 + 8, 8 );
383
CPL_LSBPTR64( &dfValue );
384
osFValue.Printf( "%.15g", dfValue );
385
poDS->SetMetadataItem( "MINOR_F", osFValue );
387
memcpy( &dfValue, achHeader + 9*16 + 8, 8 );
388
CPL_LSBPTR64( &dfValue );
389
osFValue.Printf( "%.15g", dfValue );
390
poDS->SetMetadataItem( "MAJOR_T", osFValue );
392
memcpy( &dfValue, achHeader + 10*16 + 8, 8 );
393
CPL_LSBPTR64( &dfValue );
394
osFValue.Printf( "%.15g", dfValue );
395
poDS->SetMetadataItem( "MINOR_T", osFValue );
397
/* ==================================================================== */
398
/* Loop over grids. */
399
/* ==================================================================== */
401
vsi_l_offset nGridOffset = sizeof(achHeader);
403
for( iGrid = 0; iGrid < nSubFileCount; iGrid++ )
409
VSIFSeekL( poDS->fpImage, nGridOffset, SEEK_SET );
410
if (VSIFReadL( achHeader, 11, 16, poDS->fpImage ) != 16)
412
CPLError(CE_Failure, CPLE_AppDefined,
413
"Cannot read header for subfile %d", iGrid);
418
for( i = 4; i <= 9; i++ )
419
CPL_LSBPTR64( achHeader + i*16 + 8 );
421
CPL_LSBPTR32( achHeader + 10*16 + 8 );
423
memcpy( &nGSCount, achHeader + 10*16 + 8, 4 );
425
osSubName.assign( achHeader + 8, 8 );
428
// If this is our target grid, open it as a dataset.
429
if( iTargetGrid == iGrid || (iTargetGrid == -1 && iGrid == 0) )
431
if( !poDS->OpenGrid( achHeader, nGridOffset ) )
438
// If we are opening the file as a whole, list subdatasets.
439
if( iTargetGrid == -1 )
441
CPLString osKey, osValue;
443
osKey.Printf( "SUBDATASET_%d_NAME", iGrid );
444
osValue.Printf( "NTv2:%d:%s", iGrid, osFilename.c_str() );
445
poDS->SetMetadataItem( osKey, osValue, "SUBDATASETS" );
447
osKey.Printf( "SUBDATASET_%d_DESC", iGrid );
448
osValue.Printf( "%s", osSubName.c_str() );
449
poDS->SetMetadataItem( osKey, osValue, "SUBDATASETS" );
452
nGridOffset += (11+(vsi_l_offset)nGSCount) * 16;
455
/* -------------------------------------------------------------------- */
456
/* Initialize any PAM information. */
457
/* -------------------------------------------------------------------- */
458
poDS->SetDescription( poOpenInfo->pszFilename );
461
/* -------------------------------------------------------------------- */
462
/* Check for overviews. */
463
/* -------------------------------------------------------------------- */
464
poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
469
/************************************************************************/
472
/* Note that the caller will already have byte swapped needed */
473
/* portions of the header. */
474
/************************************************************************/
476
int NTv2Dataset::OpenGrid( char *pachHeader, vsi_l_offset nGridOffset )
479
this->nGridOffset = nGridOffset;
481
/* -------------------------------------------------------------------- */
482
/* Read the grid header. */
483
/* -------------------------------------------------------------------- */
484
double s_lat, n_lat, e_long, w_long, lat_inc, long_inc;
486
CaptureMetadataItem( pachHeader + 0*16 );
487
CaptureMetadataItem( pachHeader + 1*16 );
488
CaptureMetadataItem( pachHeader + 2*16 );
489
CaptureMetadataItem( pachHeader + 3*16 );
491
memcpy( &s_lat, pachHeader + 4*16 + 8, 8 );
492
memcpy( &n_lat, pachHeader + 5*16 + 8, 8 );
493
memcpy( &e_long, pachHeader + 6*16 + 8, 8 );
494
memcpy( &w_long, pachHeader + 7*16 + 8, 8 );
495
memcpy( &lat_inc, pachHeader + 8*16 + 8, 8 );
496
memcpy( &long_inc, pachHeader + 9*16 + 8, 8 );
501
nRasterXSize = (int) floor((e_long - w_long) / long_inc + 1.5);
502
nRasterYSize = (int) floor((n_lat - s_lat) / lat_inc + 1.5);
504
if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize))
507
/* -------------------------------------------------------------------- */
508
/* Create band information object. */
510
/* We use unusual offsets to remap from bottom to top, to top */
511
/* to bottom orientation, and also to remap east to west, to */
513
/* -------------------------------------------------------------------- */
516
for( iBand = 0; iBand < 4; iBand++ )
518
RawRasterBand *poBand =
519
new RawRasterBand( this, iBand+1, fpImage,
520
nGridOffset + 4*iBand + 11*16
521
+ (nRasterXSize-1) * 16
522
+ (nRasterYSize-1) * 16 * nRasterXSize,
523
-16, -16 * nRasterXSize,
524
GDT_Float32, CPL_IS_LSB, TRUE, FALSE );
525
SetBand( iBand+1, poBand );
528
GetRasterBand(1)->SetDescription( "Latitude Offset" );
529
GetRasterBand(2)->SetDescription( "Longitude Offset" );
530
GetRasterBand(3)->SetDescription( "Latitude Error" );
531
GetRasterBand(4)->SetDescription( "Longitude Error" );
533
/* -------------------------------------------------------------------- */
534
/* Setup georeferencing. */
535
/* -------------------------------------------------------------------- */
536
adfGeoTransform[0] = (w_long - long_inc*0.5) / 3600.0;
537
adfGeoTransform[1] = long_inc / 3600.0;
538
adfGeoTransform[2] = 0.0;
539
adfGeoTransform[3] = (n_lat + lat_inc*0.5) / 3600.0;
540
adfGeoTransform[4] = 0.0;
541
adfGeoTransform[5] = (-1 * lat_inc) / 3600.0;
546
/************************************************************************/
547
/* CaptureMetadataItem() */
548
/************************************************************************/
550
void NTv2Dataset::CaptureMetadataItem( char *pszItem )
553
CPLString osKey, osValue;
555
osKey.assign( pszItem, 8 );
556
osValue.assign( pszItem+8, 8 );
558
SetMetadataItem( osKey.Trim(), osValue.Trim() );
561
/************************************************************************/
562
/* GetGeoTransform() */
563
/************************************************************************/
565
CPLErr NTv2Dataset::GetGeoTransform( double * padfTransform )
568
memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 );
572
/************************************************************************/
573
/* SetGeoTransform() */
574
/************************************************************************/
576
CPLErr NTv2Dataset::SetGeoTransform( double * padfTransform )
579
if( eAccess == GA_ReadOnly )
581
CPLError( CE_Failure, CPLE_NoWriteAccess,
582
"Unable to update geotransform on readonly file." );
586
if( padfTransform[2] != 0.0 || padfTransform[4] != 0.0 )
588
CPLError( CE_Failure, CPLE_AppDefined,
589
"Rotated and sheared geotransforms not supported for NTv2.");
593
memcpy( adfGeoTransform, padfTransform, sizeof(double)*6 );
595
/* -------------------------------------------------------------------- */
596
/* Update grid header. */
597
/* -------------------------------------------------------------------- */
599
char achHeader[11*16];
602
VSIFSeekL( fpImage, nGridOffset, SEEK_SET );
603
VSIFReadL( achHeader, 11, 16, fpImage );
606
dfValue = 3600 * (adfGeoTransform[3] + (nRasterYSize-0.5) * adfGeoTransform[5]);
607
CPL_LSBPTR64( &dfValue );
608
memcpy( achHeader + 4*16 + 8, &dfValue, 8 );
611
dfValue = 3600 * (adfGeoTransform[3] + 0.5 * adfGeoTransform[5]);
612
CPL_LSBPTR64( &dfValue );
613
memcpy( achHeader + 5*16 + 8, &dfValue, 8 );
616
dfValue = -3600 * (adfGeoTransform[0] + (nRasterXSize-0.5)*adfGeoTransform[1]);
617
CPL_LSBPTR64( &dfValue );
618
memcpy( achHeader + 6*16 + 8, &dfValue, 8 );
621
dfValue = -3600 * (adfGeoTransform[0] + 0.5 * adfGeoTransform[1]);
622
CPL_LSBPTR64( &dfValue );
623
memcpy( achHeader + 7*16 + 8, &dfValue, 8 );
626
dfValue = -3600 * adfGeoTransform[5];
627
CPL_LSBPTR64( &dfValue );
628
memcpy( achHeader + 8*16 + 8, &dfValue, 8 );
631
dfValue = 3600 * adfGeoTransform[1];
632
CPL_LSBPTR64( &dfValue );
633
memcpy( achHeader + 9*16 + 8, &dfValue, 8 );
635
// write grid header.
636
VSIFSeekL( fpImage, nGridOffset, SEEK_SET );
637
VSIFWriteL( achHeader, 11, 16, fpImage );
643
/************************************************************************/
644
/* GetProjectionRef() */
645
/************************************************************************/
647
const char *NTv2Dataset::GetProjectionRef()
650
return SRS_WKT_WGS84;
653
/************************************************************************/
655
/************************************************************************/
657
GDALDataset *NTv2Dataset::Create( const char * pszFilename,
658
int nXSize, int nYSize, int nBands,
660
char ** papszOptions )
663
if( eType != GDT_Float32 )
665
CPLError(CE_Failure, CPLE_AppDefined,
666
"Attempt to create NTv2 file with unsupported data type '%s'.",
667
GDALGetDataTypeName( eType ) );
671
/* -------------------------------------------------------------------- */
672
/* Are we extending an existing file? */
673
/* -------------------------------------------------------------------- */
675
GUInt32 nNumFile = 1;
677
int bAppend = CSLFetchBoolean(papszOptions,"APPEND_SUBDATASET",FALSE);
679
/* -------------------------------------------------------------------- */
680
/* Try to open or create file. */
681
/* -------------------------------------------------------------------- */
683
fp = VSIFOpenL( pszFilename, "rb+" );
685
fp = VSIFOpenL( pszFilename, "wb" );
689
CPLError( CE_Failure, CPLE_OpenFailed,
690
"Attempt to open/create file `%s' failed.\n",
695
/* -------------------------------------------------------------------- */
696
/* Create a file level header if we are creating new. */
697
/* -------------------------------------------------------------------- */
698
char achHeader[11*16];
699
const char *pszValue;
703
memset( achHeader, 0, sizeof(achHeader) );
705
memcpy( achHeader + 0*16, "NUM_OREC", 8 );
706
achHeader[ 0*16 + 8] = 0xb;
708
memcpy( achHeader + 1*16, "NUM_SREC", 8 );
709
achHeader[ 1*16 + 8] = 0xb;
711
memcpy( achHeader + 2*16, "NUM_FILE", 8 );
712
achHeader[ 2*16 + 8] = 0x1;
714
memcpy( achHeader + 3*16, "GS_TYPE ", 16 );
715
pszValue = CSLFetchNameValueDef( papszOptions, "GS_TYPE", "SECONDS");
716
memcpy( achHeader + 3*16+8, pszValue, MIN(16,strlen(pszValue)) );
718
memcpy( achHeader + 4*16, "VERSION ", 16 );
719
pszValue = CSLFetchNameValueDef( papszOptions, "VERSION", "" );
720
memcpy( achHeader + 4*16+8, pszValue, MIN(16,strlen(pszValue)) );
722
memcpy( achHeader + 5*16, "SYSTEM_F ", 16 );
723
pszValue = CSLFetchNameValueDef( papszOptions, "SYSTEM_F", "" );
724
memcpy( achHeader + 5*16+8, pszValue, MIN(16,strlen(pszValue)) );
726
memcpy( achHeader + 6*16, "SYSTEM_T ", 16 );
727
pszValue = CSLFetchNameValueDef( papszOptions, "SYSTEM_T", "" );
728
memcpy( achHeader + 6*16+8, pszValue, MIN(16,strlen(pszValue)) );
730
memcpy( achHeader + 7*16, "MAJOR_F ", 8);
731
memcpy( achHeader + 8*16, "MINOR_F ", 8 );
732
memcpy( achHeader + 9*16, "MAJOR_T ", 8 );
733
memcpy( achHeader + 10*16, "MINOR_T ", 8 );
735
VSIFWriteL( achHeader, 1, sizeof(achHeader), fp );
738
/* -------------------------------------------------------------------- */
739
/* Otherwise update the header with an increased subfile count, */
740
/* and advanced to the last record of the file. */
741
/* -------------------------------------------------------------------- */
744
VSIFSeekL( fp, 2*16 + 8, SEEK_SET );
745
VSIFReadL( &nNumFile, 1, 4, fp );
746
CPL_LSBPTR32( &nNumFile );
750
CPL_LSBPTR32( &nNumFile );
751
VSIFSeekL( fp, 2*16 + 8, SEEK_SET );
752
VSIFWriteL( &nNumFile, 1, 4, fp );
756
VSIFSeekL( fp, 0, SEEK_END );
757
nEnd = VSIFTellL( fp );
758
VSIFSeekL( fp, nEnd-16, SEEK_SET );
761
/* -------------------------------------------------------------------- */
762
/* Write the grid header. */
763
/* -------------------------------------------------------------------- */
764
memset( achHeader, 0, sizeof(achHeader) );
766
memcpy( achHeader + 0*16, "SUB_NAME ", 16 );
767
pszValue = CSLFetchNameValueDef( papszOptions, "SUB_NAME", "" );
768
memcpy( achHeader + 0*16+8, pszValue, MIN(16,strlen(pszValue)) );
770
memcpy( achHeader + 1*16, "PARENT ", 16 );
771
pszValue = CSLFetchNameValueDef( papszOptions, "PARENT", "NONE" );
772
memcpy( achHeader + 1*16+8, pszValue, MIN(16,strlen(pszValue)) );
774
memcpy( achHeader + 2*16, "CREATED ", 16 );
775
pszValue = CSLFetchNameValueDef( papszOptions, "CREATED", "" );
776
memcpy( achHeader + 2*16+8, pszValue, MIN(16,strlen(pszValue)) );
778
memcpy( achHeader + 3*16, "UPDATED ", 16 );
779
pszValue = CSLFetchNameValueDef( papszOptions, "UPDATED", "" );
780
memcpy( achHeader + 3*16+8, pszValue, MIN(16,strlen(pszValue)) );
784
memcpy( achHeader + 4*16, "S_LAT ", 8 );
786
CPL_LSBPTR64( &dfValue );
787
memcpy( achHeader + 4*16 + 8, &dfValue, 8 );
789
memcpy( achHeader + 5*16, "N_LAT ", 8 );
791
CPL_LSBPTR64( &dfValue );
792
memcpy( achHeader + 5*16 + 8, &dfValue, 8 );
794
memcpy( achHeader + 6*16, "E_LONG ", 8 );
795
dfValue = -1*(nXSize-1);
796
CPL_LSBPTR64( &dfValue );
797
memcpy( achHeader + 6*16 + 8, &dfValue, 8 );
799
memcpy( achHeader + 7*16, "W_LONG ", 8 );
801
CPL_LSBPTR64( &dfValue );
802
memcpy( achHeader + 7*16 + 8, &dfValue, 8 );
804
memcpy( achHeader + 8*16, "LAT_INC ", 8 );
806
CPL_LSBPTR64( &dfValue );
807
memcpy( achHeader + 8*16 + 8, &dfValue, 8 );
809
memcpy( achHeader + 9*16, "LONG_INC", 8 );
810
memcpy( achHeader + 9*16 + 8, &dfValue, 8 );
812
memcpy( achHeader + 10*16, "GS_COUNT", 8 );
813
GUInt32 nGSCount = nXSize * nYSize;
814
CPL_LSBPTR32( &nGSCount );
815
memcpy( achHeader + 10*16+8, &nGSCount, 4 );
817
VSIFWriteL( achHeader, 1, sizeof(achHeader), fp );
819
/* -------------------------------------------------------------------- */
820
/* Write zeroed grid data. */
821
/* -------------------------------------------------------------------- */
824
memset( achHeader, 0, 16 );
826
// Use -1 (0x000080bf) as the default error value.
827
memset( achHeader + 10, 0x80, 1 );
828
memset( achHeader + 11, 0xbf, 1 );
829
memset( achHeader + 14, 0x80, 1 );
830
memset( achHeader + 15, 0xbf, 1 );
832
for( i = 0; i < nXSize * nYSize; i++ )
833
VSIFWriteL( achHeader, 1, 16, fp );
835
/* -------------------------------------------------------------------- */
836
/* Write the end record. */
837
/* -------------------------------------------------------------------- */
838
memset( achHeader, 0, 16 );
839
memcpy( achHeader, "END ", 8 );
840
VSIFWriteL( achHeader, 1, 16, fp );
844
return (GDALDataset *) GDALOpen( pszFilename, GA_Update );
847
CPLString osSubDSName;
848
osSubDSName.Printf( "NTv2:%d:%s", nNumFile-1, pszFilename );
849
return (GDALDataset *) GDALOpen( osSubDSName, GA_Update );
853
/************************************************************************/
854
/* GDALRegister_NTv2() */
855
/************************************************************************/
857
void GDALRegister_NTv2()
860
GDALDriver *poDriver;
862
if( GDALGetDriverByName( "NTv2" ) == NULL )
864
poDriver = new GDALDriver();
866
poDriver->SetDescription( "NTv2" );
867
poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
868
"NTv2 Datum Grid Shift" );
869
poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gsb" );
870
poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
872
poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
875
poDriver->pfnOpen = NTv2Dataset::Open;
876
poDriver->pfnIdentify = NTv2Dataset::Identify;
877
poDriver->pfnCreate = NTv2Dataset::Create;
879
GetGDALDriverManager()->RegisterDriver( poDriver );