00001
00073
00074
00075
00076
00077 if( poOpenInfo->fp == NULL || poOpenInfo->nHeaderBytes < 50 )
00078 return NULL;
00079
00080
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
00093
00094 JDEMDataset *poDS;
00095
00096 poDS = new JDEMDataset();
00097
00098 poDS->fp = poOpenInfo->fp;
00099 poOpenInfo->fp = NULL;
00100
00101
00102
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
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;
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
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
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
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
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
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
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 ** )
00784
00785 {
00786 char *pszAuxFilename;
00787
00788
00789
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
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
00819
00820
00821 VSIFWrite( (void *) "\0\0", 2, 1, fp );
00822 VSIFClose( fp );
00823
00824
00825
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
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
00855
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
00868
00869 VSIFPrintf( fp, "RawDefinition: %d %d %d\n",
00870 nXSize, nYSize, nBands );
00871
00872
00873
00874
00875
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
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