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