Main Page Class Hierarchy Compound List File List Compound Members File Members Related Pages

# gdal_drivertut.dox

    00001
    00073 /*      Before trying JDEMOpen() we first verify that there is at       */
    00074 /*      least one "\n#keyword" type signature in the first chunk of     */
    00075 /*      the file.                                                       */
    00076 /* -------------------------------------------------------------------- */
    00077     if( poOpenInfo->fp == NULL || poOpenInfo->nHeaderBytes < 50 )
    00078         return NULL;
    00079
    00080     /* check if century values seem reasonable */
    00081     if( (!EQUALN((char *)poOpenInfo->pabyHeader+11,"19",2)
    00082           && !EQUALN((char *)poOpenInfo->pabyHeader+11,"20",2))
    00083         || (!EQUALN((char *)poOpenInfo->pabyHeader+15,"19",2)
    00084              && !EQUALN((char *)poOpenInfo->pabyHeader+15,"20",2))
    00085         || (!EQUALN((char *)poOpenInfo->pabyHeader+19,"19",2)
    00086              && !EQUALN((char *)poOpenInfo->pabyHeader+19,"20",2)) )
    00087     {
    00088         return NULL;
    00089     }
    00090
    00091 /* -------------------------------------------------------------------- */
    00092 /*      Create a corresponding GDALDataset.                             */
    00093 /* -------------------------------------------------------------------- */
    00094     JDEMDataset         *poDS;
    00095
    00096     poDS = new JDEMDataset();
    00097
    00098     poDS->fp = poOpenInfo->fp;
    00099     poOpenInfo->fp = NULL;
    00100
    00101 /* -------------------------------------------------------------------- */
    00102 /*      Read the header.                                                */
    00103 /* -------------------------------------------------------------------- */
    00104     VSIFSeek( poDS->fp, 0, SEEK_SET );
    00105     VSIFRead( poDS->abyHeader, 1, 1012, poDS->fp );
    00106
    00107     poDS->nRasterXSize = JDEMGetField( (char *) poDS->abyHeader + 23, 3 );
    00108     poDS->nRasterYSize = JDEMGetField( (char *) poDS->abyHeader + 26, 3 );
    00109
    00110 /* -------------------------------------------------------------------- */
    00111 /*      Create band information objects.                                */
    00112 /* -------------------------------------------------------------------- */
    00113     poDS->nBands = 1;
    00114     poDS->SetBand( 1, new JDEMRasterBand( poDS, 1 ));
    00115
    00116     return( poDS );
    00117 }
    00118 \endverbatim
    00119
    00120 The first step in any database Open function is to verify that the file
    00121 being passed is in fact of the type this driver is for.  It is important
    00122 to realize that each driver's Open function is called in turn till one
    00123 succeeds.  Drivers must quitly return NULL if the passed file is not of
    00124 their format.  They should only produce an error if the file does appear to
    00125 be of their supported format, but is for some reason unsupported or corrupt.
    00126
    00127 The information on the file to be opened is passed in contained in a
    00128 GDALOpenInfo object.  The GDALOpenInfo includes the following public
    00129 data members:
    00130
    00131 \code
    00132     char        *pszFilename;
    00133
    00134     GDALAccess  eAccess; /* GA_ReadOnly or GA_Update */
    00135
    00136     GBool       bStatOK;
    00137     VSIStatBuf  sStat;
    00138
    00139     FILE        *fp;
    00140
    00141     int         nHeaderBytes;
    00142     GByte       *pabyHeader;
    00143 \endcode
    00144
    00145 The driver can inspect these to establish if the file is supported.  If the
    00146 pszFilename refers to an object in the file system, the <b>bStatOK</b> flag
    00147 will be set, and the <b>sStat</b> structure will contain normal stat()
    00148 information about the object (be it directory, file, device).  If the object
    00149 is a regular readable file, the <b>fp</b> will be non-NULL, and can be used
    00150 for reads on the file (please use the VSI stdio functions from
    00151 cpl_vsi.h).  As well, if the file was successfully opened, the first kilobyte
    00152 or so is read in, and put in <b>pabyHeader</b>, with the exact size in
    00153 <b>nHeaderBytes</b>.
    00154
    00155 In this typical testing example it is verified that the file was successfully
    00156 opened, that we have at least enough header information to perform our test,
    00157 and that various parts of the header are as expected for this format.  In
    00158 this case, there are no <i>magic</i> numbers for JDEM format so we check
    00159 various date fields to ensure they have reasonable century values.  If the
    00160 test fails, we quietly return NULL indicating this file isn't of our supported
    00161 format.
    00162
    00163 \code
    00164     if( poOpenInfo->fp == NULL || poOpenInfo->nHeaderBytes < 50 )
    00165         return NULL;
    00166
    00167     /* check if century values seem reasonable */
    00168     if( (!EQUALN((char *)poOpenInfo->pabyHeader+11,"19",2)
    00169           && !EQUALN((char *)poOpenInfo->pabyHeader+11,"20",2))
    00170         || (!EQUALN((char *)poOpenInfo->pabyHeader+15,"19",2)
    00171              && !EQUALN((char *)poOpenInfo->pabyHeader+15,"20",2))
    00172         || (!EQUALN((char *)poOpenInfo->pabyHeader+19,"19",2)
    00173              && !EQUALN((char *)poOpenInfo->pabyHeader+19,"20",2)) )
    00174     {
    00175         return NULL;
    00176     }
    00177 \endcode
    00178
    00179 It is important to make the <i>is this my format</i> test as stringent as
    00180 possible.  In this particular case the test is weak, and a file that happened
    00181 to have 19s or 20s at a few locations could be erroneously recognised as
    00182 JDEM format, causing it to not be handled properly.
    00183
    00184 Once we are satisfied that the file is of our format, we need to create
    00185 an instance of the database class in which we will set various information
    00186 of interest.
    00187
    00188 \code
    00189     JDEMDataset         *poDS;
    00190
    00191     poDS = new JDEMDataset();
    00192
    00193     poDS->fp = poOpenInfo->fp;
    00194     poOpenInfo->fp = NULL;
    00195 \endcode
    00196
    00197 Generally at this point we would open the file, to acquire a file handle
    00198 for the dataset; however, if read-only access is sufficient it is permitted
    00199 to <b>assume ownership</b> of the FILE * from the GDALOpenInfo object.
    00200 Just ensure that it is set to NULL in the GDALOpenInfo to avoid having it
    00201 get closed twice.  It is also important to note that the state of the
    00202 FILE * adopted is indeterminate.  Ensure that the current location is reset
    00203 with VSIFSeek() before assuming you can read from it.   This is accomplished
    00204 in the following statements which reset the file and read the header.
    00205
    00206 \code
    00207     VSIFSeek( poDS->fp, 0, SEEK_SET );
    00208     VSIFRead( poDS->abyHeader, 1, 1012, poDS->fp );
    00209 \endcode
    00210
    00211 Next the X and Y size are extracted from the header. The nRasterXSize and
    00212 nRasterYSize are data fields inherited from the GDALDataset base class, and
    00213 must be set by the Open() method.
    00214
    00215 \code
    00216     poDS->nRasterXSize = JDEMGetField( (char *) poDS->abyHeader + 23, 3 );
    00217     poDS->nRasterYSize = JDEMGetField( (char *) poDS->abyHeader + 26, 3 );
    00218 \endcode
    00219
    00220 Finally, all the bands related to this dataset must be attached using
    00221 the SetBand() method.  We will explore the JDEMRasterBand() class shortly.
    00222
    00223 \code
    00224     poDS->SetBand( 1, new JDEMRasterBand( poDS, 1 ));
    00225
    00226     return( poDS );
    00227 \endcode
    00228
    00229 <h2><a name="rasterband">Implementing the RasterBand</a></h2>
    00230
    00231 Similar to the customized JDEMDataset class subclassed from GDALDataset,
    00232 we also need to declare and implement a customized JDEMRasterBand derived
    00233 from GDALRasterBand for access to the band(s) of the JDEM file.  For
    00234 JDEMRasterBand the declaration looks like this:
    00235
    00236 \code
    00237 class JDEMRasterBand : public GDALRasterBand
    00238 {
    00239   public:
    00240                 JDEMRasterBand( JDEMDataset *, int );
    00241     virtual CPLErr IReadBlock( int, int, void * );
    00242 };
    00243 \endcode
    00244
    00245 The constructor may have any signature, and is only called from the Open()
    00246 method.  Other virtual methods, such as IReadBlock() must be exactly
    00247 matched to the method signature in gdal_priv.h.
    00248
    00249 The constructor implementation looks like this:
    00250
    00251 \code
    00252 JDEMRasterBand::JDEMRasterBand( JDEMDataset *poDS, int nBand )
    00253
    00254 {
    00255     this->poDS = poDS;
    00256     this->nBand = nBand;
    00257
    00258     eDataType = GDT_Float32;
    00259
    00260     nBlockXSize = poDS->GetRasterXSize();
    00261     nBlockYSize = 1;
    00262 }
    00263 \endcode
    00264
    00265 The following data members are inherited from GDALRasterBand, and should
    00266 generally be set in the band constructor.
    00267
    00268 <ul>
    00269 <li> <b>poDS</b>: Pointer to the parent GDALDataset.
    00270 <li> <b>nBand</b>: The band number within the dataset.
    00271 <li> <b>eDataType</b>: The data type of pixels in this band.
    00272 <li> <b>nBlockXSize</b>: The width of one block in this band.
    00273 <li> <b>nBlockYSize</b>: The height of one block in this band.
    00274 </ul>
    00275
    00276 The full set of possible GDALDataType values are declared in gdal.h, and
    00277 include GDT_Byte, GDT_UInt16, GDT_Int16, and GDT_Float32.  The block size is
    00278 used to establish a <i>natural</i> or efficient block size to access the data
    00279 with.  For tiled datasets this will be the size of a tile, while for most
    00280 other datasets it will be one scanline, as in this case.
    00281
    00282 Next we see the implementation of the code that actually reads the image
    00283 data, IReadBlock().
    00284
    00285 \code
    00286 CPLErr JDEMRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
    00287                                   void * pImage )
    00288
    00289 {
    00290     JDEMDataset *poGDS = (JDEMDataset *) poDS;
    00291     char        *pszRecord;
    00292     int         nRecordSize = nBlockXSize*5 + 9 + 2;
    00293     int         i;
    00294
    00295     VSIFSeek( poGDS->fp, 1011 + nRecordSize*nBlockYOff, SEEK_SET );
    00296
    00297     pszRecord = (char *) CPLMalloc(nRecordSize);
    00298     VSIFRead( pszRecord, 1, nRecordSize, poGDS->fp );
    00299
    00300     if( !EQUALN((char *) poGDS->abyHeader,pszRecord,6) )
    00301     {
    00302         CPLFree( pszRecord );
    00303
    00304         CPLError( CE_Failure, CPLE_AppDefined,
    00305                   "JDEM Scanline corrupt.  Perhaps file was not transferred\n"
    00306                   "in binary mode?" );
    00307         return CE_Failure;
    00308     }
    00309
    00310     if( JDEMGetField( pszRecord + 6, 3 ) != nBlockYOff + 1 )
    00311     {
    00312         CPLFree( pszRecord );
    00313
    00314         CPLError( CE_Failure, CPLE_AppDefined,
    00315                   "JDEM scanline out of order, JDEM driver does not\n"
    00316                   "currently support partial datasets." );
    00317         return CE_Failure;
    00318     }
    00319
    00320     for( i = 0; i < nBlockXSize; i++ )
    00321         ((float *) pImage)[i] = JDEMGetField( pszRecord + 9 + 5 * i, 5) * 0.1;
    00322
    00323     return CE_None;
    00324 }
    00325 \endcode
    00326
    00327 Key items to note are:
    00328
    00329 <ul>
    00330 <li> It is typical to cast the GDALRasterBand::poDS member to the derived
    00331 type of the owning dataset.  If your RasterBand class will need priveledged
    00332 access to the owning dataset object, ensure it is declared as a friend (omitted
    00333 above for brevity).
    00334
    00335 <li> If an error occurs, report it with CPLError(), and return CE_Failure.
    00336 Otherwise return CE_None.
    00337
    00338 <li> The pImage buffer should be filled with one block of data.  The block
    00339 is the size declared in nBlockXSize and nBlockYSize for the raster band.  The
    00340 type of the data within pImage should match the type declared in
    00341 eDataType in the raster band object.
    00342
    00343 <li> The nBlockXOff and nBlockYOff are block offsets, so with 128x128 tiled
    00344 datasets values of 1 and 1 would indicate the block going from (128,128) to
    00345 (255,255) should be loaded.
    00346
    00347 </ul>
    00348
    00349 <h2><a name="driver">The Driver</a></h2>
    00350
    00351 While the JDEMDataset and JDEMRasterBand are now ready to use to read image
    00352 data, it still isn't clear how the GDAL system knows about the new driver.
    00353 This is accomplished via the GDALDriverManager.  To register our format we
    00354 implement a registration function:
    00355
    00356 \code
    00357 CPL_C_START
    00358 void    GDALRegister_JDEM(void);
    00359 CPL_C_END
    00360
    00361 ...
    00362
    00363 void GDALRegister_JDEM()
    00364
    00365 {
    00366     GDALDriver  *poDriver;
    00367
    00368     if( GDALGetDriverByName( "JDEM" ) == NULL )
    00369     {
    00370         poDriver = new GDALDriver();
    00371
    00372         poDriver->SetDescription( "JDEM" );
    00373         poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
    00374                                    "Japanese DEM (.mem)" );
    00375         poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
    00376                                    "frmt_various.html#JDEM" );
    00377         poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "mem" );
    00378
    00379         poDriver->pfnOpen = JDEMDataset::Open;
    00380
    00381         GetGDALDriverManager()->RegisterDriver( poDriver );
    00382     }
    00383 }
    00384 \endcode
    00385
    00386 The registration function will create an instance of a GDALDriver object
    00387 when first called, and register it with the GDALDriverManager.  The
    00388 following fields can be set in the driver before
    00389 registering it with the GDALDriverManager().
    00390
    00391 <ul>
    00392 <li> The description is the short name for the format.  This is a unique
    00393 name for this format, often used to identity the driver in scripts and
    00394 commandline programs.  Normally 3-5 characters in length, and matching the
    00395 prefix of the format classes. (manditory)
    00396
    00397 <li> GDAL_DMD_LONGNAME: A longer descriptive name for the file format,
    00398 but still no longer than 50-60 characters. (manditory)
    00399
    00400 <li> GDAL_DMD_HELPTOPIC: The name of a help topic to display for this driver,
    00401 if any.  In this case JDEM format is contained within the various format
    00402 web page held in gdal/html.  (optional)
    00403
    00404 <li> GDAL_DMD_EXTENSION: The extension used for files of this type.  If more
    00405 than one pick the primary extension, or none at all. (optional)
    00406
    00407 <li> GDAL_DMD_MIMETYPE: The standard mime type for this file format, such as
    00408 "image/png". (optional)
    00409
    00410 <li> GDAL_DMD_CREATIONOPTIONLIST: There is evolving work on mechanisms
    00411 to describe creation options.  See the geotiff driver for an example of
    00412 this.  (optional)
    00413
    00414 <li> GDAL_DMD_CREATIONDATATYPES: A list of space separated data types
    00415 supported by this create when creating new datasets.  If a Create() method
    00416 exists, these will be will supported.  If a CreateCopy() method exists, this
    00417 will be a list of types that can be losslessly exported but it may include
    00418 weaker datatypes than the type eventually written.  For instance, a format
    00419 with a CreateCopy() method, and that always writes Float32 might also list
    00420 Byte, Int16, and UInt16 since they can losslessly translated to Float32.  An
    00421 example value might be "Byte Int16 UInt16". (required - if creation supported)
    00422
    00423 <li> pfnOpen: The function to call to try opening files of this format.
    00424 (optional)
    00425
    00426 <li> pfnCreate: The function to call to create new updatable datasets of this
    00427 format. (optional)
    00428
    00429 <li> pfnCreateCopy: The function to call to create a new dataset of this format
    00430 copied from another source, but not necessary updatable.  (optional)
    00431
    00432 <li> pfnDelete: The function to call to delete a dataset of this format.
    00433 (optional)
    00434
    00435 <li> pfnUnloadDriver: A function called only when the driver is destroyed.
    00436 Could be used to cleanup data at the driver level.  Rarely used.  (optional)
    00437
    00438 </ul>
    00439
    00440 <h2><a name="addingdriver">Adding Driver to GDAL Tree</a></h2>
    00441
    00442 Note that the GDALRegister_JDEM() method must be called by the higher
    00443 level program in order to have access to the JDEM driver.  Normal practice
    00444 when writing new drivers is to:
    00445
    00446 <ol>
    00447 <li> Add a driver directory under gdal/frmts, with the directory name the same
    00448 as the short name.
    00449
    00450 <li> Add a GNUmakefile and makefile.vc in that directory modelled on those
    00451 from other similar directories (ie. the jdem directory).
    00452
    00453 <li> Add the module with the dataset, and rasterband implementation.
    00454 Generally this is called <short_name>dataset.cpp, with all the GDAL specifc
    00455 code in one file, though that is not required.
    00456
    00457 <li> Add the registration entry point declaration (ie. GDALRegister_JDEM()) to
    00458 gdal/core/gdal_frmts.h.
    00459
    00460 <li> Add a call to the registration function to frmts/gdalallregister.c,
    00461 protected by an appropriate #ifdef.
    00462
    00463 <li> Add the format short name to the GDAL_FORMATS macro in
    00464 GDALmake.opt.in (and to GDALmake.opt).
    00465
    00466 <li> Add a format specific item to the EXTRAFLAGS macro in frmts/makefile.vc.
    00467 </ol>
    00468
    00469 Once this is all done, it should be possible to rebuild GDAL, and have
    00470 the new format available in all the utilities.  The gdalinfo utility can be
    00471 used to test that opening and reporting on the format is working, and the
    00472 gdal_translate utility can be used to test image reading.
    00473
    00474 <h2><a name="georef">Adding Georeferencing</a></h2>
    00475
    00476 Now we will take the example a step forward, adding georeferencing support.
    00477 We add the following two virtual method overrides to JDEMDataset, taking
    00478 care to exactly match the signature of the method on the GDALRasterDataset
    00479 base class.
    00480
    00481 \code
    00482     CPLErr      GetGeoTransform( double * padfTransform );
    00483     const char *GetProjectionRef();
    00484 \endcode
    00485
    00486 The implementation of GetGeoTransform() just copies the usual geotransform
    00487 matrix into the supplied buffer.  Note that GetGeoTransform() may be called
    00488 alot, so it isn't generally wise to do alot of computation in it.  In many
    00489 cases the Open() will collect the geotransform, and this method will just
    00490 copy it over.  Also note that the geotransform return is based on an
    00491 anchor point at the top left corner of the top left pixel, not the center
    00492 of pixel approach used in some packages.
    00493
    00494 \code
    00495 CPLErr JDEMDataset::GetGeoTransform( double * padfTransform )
    00496
    00497 {
    00498     double      dfLLLat, dfLLLong, dfURLat, dfURLong;
    00499
    00500     dfLLLat = JDEMGetAngle( (char *) abyHeader + 29 );
    00501     dfLLLong = JDEMGetAngle( (char *) abyHeader + 36 );
    00502     dfURLat = JDEMGetAngle( (char *) abyHeader + 43 );
    00503     dfURLong = JDEMGetAngle( (char *) abyHeader + 50 );
    00504
    00505     padfTransform[0] = dfLLLong;
    00506     padfTransform[3] = dfURLat;
    00507     padfTransform[1] = (dfURLong - dfLLLong) / GetRasterXSize();
    00508     padfTransform[2] = 0.0;
    00509
    00510     padfTransform[4] = 0.0;
    00511     padfTransform[5] = -1 * (dfURLat - dfLLLat) / GetRasterYSize();
    00512
    00513
    00514     return CE_None;
    00515 }
    00516 \endcode
    00517
    00518 The GetProjectionRef() method returns a pointer to an internal string
    00519 containing a coordinate system definition in OGC WKT format.  In this case
    00520 the coordinate system is fixed for all files of this format, but in more
    00521 complex cases a definition may need to be composed on the fly, in which case
    00522 it may be helpful to use the OGRSpatialReference class to help build the
    00523 definition.
    00524
    00525 \code
    00526 const char *JDEMDataset::GetProjectionRef()
    00527
    00528 {
    00529     return( "GEOGCS[\"Tokyo\",DATUM[\"Tokyo\",SPHEROID[\"Bessel 1841\","
    00530         "6377397.155,299.1528128,AUTHORITY[\"EPSG\",7004]],TOWGS84[-148,"
    00531         "507,685,0,0,0,0],AUTHORITY[\"EPSG\",6301]],PRIMEM[\"Greenwich\","
    00532         "0,AUTHORITY[\"EPSG\",8901]],UNIT[\"DMSH\",0.0174532925199433,"
    00533         "AUTHORITY[\"EPSG\",9108]],AXIS[\"Lat\",NORTH],AXIS[\"Long\",EAST],"
    00534         "AUTHORITY[\"EPSG\",4301]]" );
    00535 }
    00536 \endcode
    00537
    00538 This completes explanation of the features of the JDEM driver.  The full
    00539 source for <a href="jdemdataset.cpp.html">jdemdataset.cpp</a> can be reviewed
    00540 as needed.
    00541
    00542 <h2><a name="overviews">Overviews</a></h2>
    00543
    00544 GDAL allows file formats to make pre-built overviews available to applications
    00545 via the GDALRasterBand::GetOverview() and related methods.  However,
    00546 implementing this is pretty involved, and goes beyond the scope of this
    00547 document for now.  The GeoTIFF driver (gdal/frmts/gtiff/geotiff.cpp) and
    00548 related source can be reviewed for an example of a file format implementing
    00549 overview reporting and creation support.
    00550
    00551 Formats can also report that they have arbitrary overviews, by overriding
    00552 the HasArbitraryOverviews() method on the GDALRasterBand, returning TRUE.
    00553 In this case the raster band object is expected to override the RasterIO()
    00554 method itself, to implement efficient access to imagery with resampling.
    00555 This is also involved, and there are alot of requirements for correct
    00556 implementation of the RasterIO() method.  An example of this can be found
    00557 in the ogdi and ecw formats.
    00558
    00559 However, by far the most common approach to implementing overviews is to
    00560 use the default support in GDAL for external overviews stored in TIFF files
    00561 with the same name as the dataset, but the extension .ovr appended.  In
    00562 order to enable reading and creation of this style of overviews it is necessary
    00563 for the GDALDataset to initialize the oOvManager object within itself.  This
    00564 is typically accomplished with a call like the following near the end of the
    00565 Open() method.
    00566
    00567 \code
    00568     poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
    00569 \endcode
    00570
    00571 This will enable default implementations for reading and creating overviews for
    00572 the format.  It is advised that this be enabled for all simple file system
    00573 based formats unless there is a custom overview mechanism to be tied into.
    00574
    00575 <h2><a name="creation">File Creation</a></h2>
    00576
    00577 There are two approaches to file creation.  The first method is called the
    00578 CreateCopy() method, and involves implementing a function that can write a
    00579 file in the output format, pulling all imagery and other information needed
    00580 from a source GDALDataset.  The second method, the dynamic creation method,
    00581 involves implementing a Create method to create the shell of the file, and
    00582 then the application writes various information by calls to set methods.
    00583
    00584 The benefits of the first method are that that all the information is available
    00585 at the point the output file is being created.  This can be especially
    00586 important when implementing file formats using external libraries which
    00587 require information like colormaps, and georeferencing information at the
    00588 point the file is created.  The other advantage of this method is that the
    00589 CreateCopy() method can read some kinds of information, such as min/max,
    00590 scaling, description and GCPs for which there are no equivelent set methods.
    00591
    00592 The benefits of the second method are that applications can create an
    00593 empty new file, and write results to it as they become available.  A complete
    00594 image of the desired data does not have to be available in advance.
    00595
    00596 For very important formats both methods may be implemented, otherwise do
    00597 whichever is simpler, or provides the required capabilities.
    00598
    00599 <h3>CreateCopy</h3>
    00600
    00601 The GDALDriver::CreateCopy() method call is passed through directly, so
    00602 that method should be consulted for details of arguments.  However, some
    00603 things to keep in mind are:
    00604
    00605 <ul>
    00606 <li> If the bStrict flag is FALSE the driver should try to do something
    00607 reasonable when it cannot exactly represent the source dataset, transforming
    00608 data types on the fly, droping georeferencing and so forth.
    00609
    00610 <li> Implementing progress reporting correctly is somewhat involved.  The
    00611 return result of the progress function needs always to be checked for
    00612 cancellation, and progress should be reported at reasonable intervals.  The
    00613 JPEGCreateCopy() method demonstrates good handling of the progress function.
    00614
    00615 <li> Special creation options should be documented in the online help.
    00616 If the options take the format "NAME=VALUE" the papszOptions list can be
    00617 manipulated with CPLFetchNameValue() as demonstrated in the handling of
    00618 the QUALITY and PROGRESSIVE flags for JPEGCreateCopy().
    00619
    00620 <li> The returned GDALDataset handle can be in ReadOnly or Update mode.
    00621 Return it in Update mode if practical, otherwise in ReadOnly mode is fine.
    00622
    00623 </ul>
    00624
    00625 The full implementation of the CreateCopy function for JPEG (which is
    00626 assigned to pfnCreateCopy in the GDALDriver object) is here.
    00627
    00628 \verbatim
    00629 static GDALDataset *
    00630 JPEGCreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
    00631                 int bStrict, char ** papszOptions,
    00632                 GDALProgressFunc pfnProgress, void * pProgressData )
    00633
    00634 {
    00635     int  nBands = poSrcDS->GetRasterCount();
    00636     int  nXSize = poSrcDS->GetRasterXSize();
    00637     int  nYSize = poSrcDS->GetRasterYSize();
    00638     int  nQuality = 75;
    00639     int  bProgressive = FALSE;
    00640
    00641 /* -------------------------------------------------------------------- */
    00642 /*      Some some rudimentary checks                                    */
    00643 /* -------------------------------------------------------------------- */
    00644     if( nBands != 1 && nBands != 3 )
    00645     {
    00646         CPLError( CE_Failure, CPLE_NotSupported,
    00647                   "JPEG driver doesn't support %d bands.  Must be 1 (grey) "
    00648                   "or 3 (RGB) bands.\n", nBands );
    00649
    00650         return NULL;
    00651     }
    00652
    00653     if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte && bStrict )
    00654     {
    00655         CPLError( CE_Failure, CPLE_NotSupported,
    00656                   "JPEG driver doesn't support data type %s. "
    00657                   "Only eight bit byte bands supported.\n",
    00658                   GDALGetDataTypeName(
    00659                       poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
    00660
    00661         return NULL;
    00662     }
    00663
    00664 /* -------------------------------------------------------------------- */
    00665 /*      What options has the user selected?                             */
    00666 /* -------------------------------------------------------------------- */
    00667     if( CSLFetchNameValue(papszOptions,"QUALITY") != NULL )
    00668     {
    00669         nQuality = atoi(CSLFetchNameValue(papszOptions,"QUALITY"));
    00670         if( nQuality < 10 || nQuality > 100 )
    00671         {
    00672             CPLError( CE_Failure, CPLE_IllegalArg,
    00673                       "QUALITY=%s is not a legal value in the range 10-100.",
    00674                       CSLFetchNameValue(papszOptions,"QUALITY") );
    00675             return NULL;
    00676         }
    00677     }
    00678
    00679     if( CSLFetchNameValue(papszOptions,"PROGRESSIVE") != NULL )
    00680     {
    00681         bProgressive = TRUE;
    00682     }
    00683
    00684 /* -------------------------------------------------------------------- */
    00685 /*      Create the dataset.                                             */
    00686 /* -------------------------------------------------------------------- */
    00687     FILE        *fpImage;
    00688
    00689     fpImage = VSIFOpen( pszFilename, "wb" );
    00690     if( fpImage == NULL )
    00691     {
    00692         CPLError( CE_Failure, CPLE_OpenFailed,
    00693                   "Unable to create jpeg file %s.\n",
    00694                   pszFilename );
    00695         return NULL;
    00696     }
    00697
    00698 /* -------------------------------------------------------------------- */
    00699 /*      Initialize JPG access to the file.                              */
    00700 /* -------------------------------------------------------------------- */
    00701     struct jpeg_compress_struct sCInfo;
    00702     struct jpeg_error_mgr sJErr;
    00703
    00704     sCInfo.err = jpeg_std_error( &sJErr );
    00705     jpeg_create_compress( &sCInfo );
    00706
    00707     jpeg_stdio_dest( &sCInfo, fpImage );
    00708
    00709     sCInfo.image_width = nXSize;
    00710     sCInfo.image_height = nYSize;
    00711     sCInfo.input_components = nBands;
    00712
    00713     if( nBands == 1 )
    00714     {
    00715         sCInfo.in_color_space = JCS_GRAYSCALE;
    00716     }
    00717     else
    00718     {
    00719         sCInfo.in_color_space = JCS_RGB;
    00720     }
    00721
    00722     jpeg_set_defaults( &sCInfo );
    00723
    00724     jpeg_set_quality( &sCInfo, nQuality, TRUE );
    00725
    00726     if( bProgressive )
    00727         jpeg_simple_progression( &sCInfo );
    00728
    00729     jpeg_start_compress( &sCInfo, TRUE );
    00730
    00731 /* -------------------------------------------------------------------- */
    00732 /*      Loop over image, copying image data.                            */
    00733 /* -------------------------------------------------------------------- */
    00734     GByte       *pabyScanline;
    00735     CPLErr      eErr;
    00736
    00737     pabyScanline = (GByte *) CPLMalloc( nBands * nXSize );
    00738
    00739     for( int iLine = 0; iLine < nYSize; iLine++ )
    00740     {
    00741         JSAMPLE      *ppSamples;
    00742
    00743         for( int iBand = 0; iBand < nBands; iBand++ )
    00744         {
    00745             GDALRasterBand * poBand = poSrcDS->GetRasterBand( iBand+1 );
    00746             eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1,
    00747                                      pabyScanline + iBand, nXSize, 1, GDT_Byte,
    00748                                      nBands, nBands * nXSize );
    00749         }
    00750
    00751         ppSamples = pabyScanline;
    00752         jpeg_write_scanlines( &sCInfo, &ppSamples, 1 );
    00753     }
    00754
    00755     CPLFree( pabyScanline );
    00756
    00757     jpeg_finish_compress( &sCInfo );
    00758     jpeg_destroy_compress( &sCInfo );
    00759
    00760     VSIFClose( fpImage );
    00761
    00762     return (GDALDataset *) GDALOpen( pszFilename, GA_ReadOnly );
    00763 }
    00764 \endverbatim
    00765
    00766 <h3>Dynamic Creation</h3>
    00767
    00768 In the case of dynamic creation, there is no source dataset.  Instead the
    00769 size, number of bands, and pixel data type of the desired file is provided
    00770 but other information (such as georeferencing, and imagery data) would be
    00771 supplied later via other method calls on the resulting GDALDataset.
    00772
    00773 The following sample implement PCI .aux labelled raw raster creation.  It
    00774 follows a common approach of creating a blank, but valid file using non-GDAL
    00775 calls, and then calling GDALOpen(,GA_Update) at the end to return a writable
    00776 file handle.  This avoids having to duplicate the various setup actions in
    00777 the Open() function.
    00778
    00779 \verbatim
    00780 GDALDataset *PAuxDataset::Create( const char * pszFilename,
    00781                                   int nXSize, int nYSize, int nBands,
    00782                                   GDALDataType eType,
    00783                                   char ** /* papszParmList */ )
    00784
    00785 {
    00786     char        *pszAuxFilename;
    00787
    00788 /* -------------------------------------------------------------------- */
    00789 /*      Verify input options.                                           */
    00790 /* -------------------------------------------------------------------- */
    00791     if( eType != GDT_Byte && eType != GDT_Float32 && eType != GDT_UInt16
    00792         && eType != GDT_Int16 )
    00793     {
    00794         CPLError( CE_Failure, CPLE_AppDefined,
    00795               "Attempt to create PCI .Aux labelled dataset with an illegal\n"
    00796               "data type (%s).\n",
    00797               GDALGetDataTypeName(eType) );
    00798
    00799         return NULL;
    00800     }
    00801
    00802 /* -------------------------------------------------------------------- */
    00803 /*      Try to create the file.                                         */
    00804 /* -------------------------------------------------------------------- */
    00805     FILE        *fp;
    00806
    00807     fp = VSIFOpen( pszFilename, "w" );
    00808
    00809     if( fp == NULL )
    00810     {
    00811         CPLError( CE_Failure, CPLE_OpenFailed,
    00812                   "Attempt to create file `%s' failed.\n",
    00813                   pszFilename );
    00814         return NULL;
    00815     }
    00816
    00817 /* -------------------------------------------------------------------- */
    00818 /*      Just write out a couple of bytes to establish the binary        */
    00819 /*      file, and then close it.                                        */
    00820 /* -------------------------------------------------------------------- */
    00821     VSIFWrite( (void *) "\0\0", 2, 1, fp );
    00822     VSIFClose( fp );
    00823
    00824 /* -------------------------------------------------------------------- */
    00825 /*      Create the aux filename.                                        */
    00826 /* -------------------------------------------------------------------- */
    00827     pszAuxFilename = (char *) CPLMalloc(strlen(pszFilename)+5);
    00828     strcpy( pszAuxFilename, pszFilename );;
    00829
    00830     for( int i = strlen(pszAuxFilename)-1; i > 0; i-- )
    00831     {
    00832         if( pszAuxFilename[i] == '.' )
    00833         {
    00834             pszAuxFilename[i] = '\0';
    00835             break;
    00836         }
    00837     }
    00838
    00839     strcat( pszAuxFilename, ".aux" );
    00840
    00841 /* -------------------------------------------------------------------- */
    00842 /*      Open the file.                                                  */
    00843 /* -------------------------------------------------------------------- */
    00844     fp = VSIFOpen( pszAuxFilename, "wt" );
    00845     if( fp == NULL )
    00846     {
    00847         CPLError( CE_Failure, CPLE_OpenFailed,
    00848                   "Attempt to create file `%s' failed.\n",
    00849                   pszAuxFilename );
    00850         return NULL;
    00851     }
    00852
    00853 /* -------------------------------------------------------------------- */
    00854 /*      We need to write out the original filename but without any      */
    00855 /*      path components in the AuxilaryTarget line.  Do so now.         */
    00856 /* -------------------------------------------------------------------- */
    00857     int         iStart;
    00858
    00859     iStart = strlen(pszFilename)-1;
    00860     while( iStart > 0 && pszFilename[iStart-1] != '/'
    00861            && pszFilename[iStart-1] != '\\' )
    00862         iStart--;
    00863
    00864     VSIFPrintf( fp, "AuxilaryTarget: %s\n", pszFilename + iStart );
    00865
    00866 /* -------------------------------------------------------------------- */
    00867 /*      Write out the raw definition for the dataset as a whole.        */
    00868 /* -------------------------------------------------------------------- */
    00869     VSIFPrintf( fp, "RawDefinition: %d %d %d\n",
    00870                 nXSize, nYSize, nBands );
    00871
    00872 /* -------------------------------------------------------------------- */
    00873 /*      Write out a definition for each band.  We always write band     */
    00874 /*      sequential files for now as these are pretty efficiently        */
    00875 /*      handled by GDAL.                                                */
    00876 /* -------------------------------------------------------------------- */
    00877     int         nImgOffset = 0;
    00878
    00879     for( int iBand = 0; iBand < nBands; iBand++ )
    00880     {
    00881         const char * pszTypeName;
    00882         int          nPixelOffset;
    00883         int          nLineOffset;
    00884
    00885         nPixelOffset = GDALGetDataTypeSize(eType)/8;
    00886         nLineOffset = nXSize * nPixelOffset;
    00887
    00888         if( eType == GDT_Float32 )
    00889             pszTypeName = "32R";
    00890         else if( eType == GDT_Int16 )
    00891             pszTypeName = "16S";
    00892         else if( eType == GDT_UInt16 )
    00893             pszTypeName = "16U";
    00894         else
    00895             pszTypeName = "8U";
    00896
    00897         VSIFPrintf( fp, "ChanDefinition-%d: %s %d %d %d %s\n",
    00898                     iBand+1, pszTypeName,
    00899                     nImgOffset, nPixelOffset, nLineOffset,
    00900 #ifdef CPL_LSB
    00901                     "Swapped"
    00902 #else
    00903                     "Unswapped"
    00904 #endif
    00905                     );
    00906
    00907         nImgOffset += nYSize * nLineOffset;
    00908     }
    00909
    00910 /* -------------------------------------------------------------------- */
    00911 /*      Cleanup                                                         */
    00912 /* -------------------------------------------------------------------- */
    00913     VSIFClose( fp );
    00914
    00915     return (GDALDataset *) GDALOpen( pszFilename, GA_Update );
    00916 }
    00917 \endverbatim
    00918
    00919 File formats supporting dynamic creation, or even just update-in-place
    00920 access also need to implement an IWriteBlock() method
    00921 on the raster band class.  It has semantics similar to IReadBlock().
    00922 As well, for various esoteric reasons, it is critical that a FlushCache()
    00923 method be implemented in the raster band destructor.  This is to ensure that
    00924 any write cache blocks for the band be flushed out before the destructor
    00925 is called.
    00926
    00927 <h2><a name="raw">RawDataset/RawRasterBand Helper Classes</a></h2>
    00928
    00929 Many file formats have the actual imagery data stored in a regular,
    00930 binary, scanline oriented format.  Rather than re-implement the access
    00931 semantics for this for each formats, there are provided RawDataset and
    00932 RawRasterBand classes declared in gdal/frmts/raw that can be utilized to
    00933 implement efficient and convenient access.
    00934
    00935 In these cases the format specific band class may not be required, or if
    00936 required it can be derived from RawRasterBand.  The dataset class should
    00937 be derived from RawDataset.
    00938
    00939 The Open() method for the dataset then instantiates raster bands passing
    00940 all the layout information to the constructor.  For instance, the PNM driver
    00941 uses the following calls to create it's raster bands.
    00942
    00943 \code
    00944     if( poOpenInfo->pabyHeader[1] == '5' )
    00945     {
    00946         poDS->SetBand(
    00947             1, new RawRasterBand( poDS, 1, poDS->fpImage,
    00948                                   iIn, 1, nWidth, GDT_Byte, TRUE ));
    00949     }
    00950     else
    00951     {
    00952         poDS->SetBand(
    00953             1, new RawRasterBand( poDS, 1, poDS->fpImage,
    00954                                   iIn, 3, nWidth*3, GDT_Byte, TRUE ));
    00955         poDS->SetBand(
    00956             2, new RawRasterBand( poDS, 2, poDS->fpImage,
    00957                                   iIn+1, 3, nWidth*3, GDT_Byte, TRUE ));
    00958         poDS->SetBand(
    00959             3, new RawRasterBand( poDS, 3, poDS->fpImage,
    00960                                   iIn+2, 3, nWidth*3, GDT_Byte, TRUE ));
    00961     }
    00962 \endcode
    00963
    00964 The RawRasterBand takes the following arguments.
    00965
    00966 <ul>
    00967 <li> <b>poDS</b>: The GDALDataset this band will be a child of.   This
    00968 dataset must be of a class derived from RawRasterDataset.
    00969 <li> <b>nBand</b>: The band it is on that dataset, 1 based.
    00970 <li> <b>fpRaw</b>: The FILE * handle to the file containing the raster data.
    00971 <li> <b>nImgOffset</b>: The byte offset to the first pixel of raster data for
    00972 the first scanline.
    00973 <li> <b>nPixelOffset</b>: The byte offset from the start of one pixel to the
    00974 start of the next within the scanline.
    00975 <li> <b>nLineOffset</b>: The byte offset from the start of one scanline to
    00976 the start of the next.
    00977 <li> <b>eDataType</b>: The GDALDataType code for the type of the data on disk.
    00978 <li> <b>bNativeOrder</b>: FALSE if the data is not in the same endianness as
    00979 the machine GDAL is running on.  The data will be automatically byte swapped.
    00980 </ul>
    00981
    00982 Simple file formats utilizing the Raw services are normally placed all within
    00983 one file in the gdal/frmts/raw directory.  There are numerous examples there
    00984 of format implementation.<p>
    00985
    00986 <h2><a name="metadata">Metadata, and Other Exotic Extensions</a></h2>
    00987
    00988 There are various other items in the GDAL data model, for which virtual
    00989 methods exist on the GDALDataset and GDALRasterBand.  They include:
    00990
    00991 <ul>
    00992 <li> <b>Metadata</b>: Name/value text values about a dataset or band.  The
    00993 GDALMajorObject (base class for GDALRasterBand and GDALDataset) has builtin
    00994 support for holding metadata, so for read access it only needs to be
    00995 set with calls to SetMetadataItem() during the Open().  The SAR_CEOS
    00996 (frmts/ceos2/sar_ceosdataset.cpp) and GeoTIFF drivers are examples of drivers
    00997 implementing readable metadata.
    00998
    00999 <li> <b>ColorTables</b>: GDT_Byte raster bands can have color tables associated
    01000 with them.  The frmts/png/pngdataset.cpp driver contains an example of a
    01001 format that supports colortables.
    01002
    01003 <li> <b>ColorInterpretation</b>: The PNG driver contains an example of a
    01004 driver that returns an indication of whether a band should be treated as
    01005 a Red, Green, Blue, Alpha or Greyscale band.
    01006
    01007 <li> <b>GCPs</b>: GDALDatasets can have a set of ground control points
    01008 associated with them (as opposed to an explicit affine transform returned by
    01009 GetGeotransform()) relating the raster to georeferenced coordinates.  The
    01010 MFF2 (gdal/frmts/raw/hkvdataset.cpp) format is a simple example of a format
    01011 supporting GCPs.
    01012
    01013 <li> <b>NoDataValue</b>: Bands with known "nodata" values can implement
    01014 the GetNoDataValue() method.  See the PAux (frmts/raw/pauxdataset.cpp) for
    01015 an example of this.
    01016
    01017 <li> <b>Category Names</b>: Classified images with names for each class can
    01018 return them using the GetCategoryNames() method though no formats currently
    01019 implement this.
    01020
    01021 </ul>
    01022
    01023 */
    01024
    01025
    01026