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->poDriver = poJDEMDriver;
00099 poDS->fp = poOpenInfo->fp;
00100 poOpenInfo->fp = NULL;
00101
00102
00103
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
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;
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
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
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
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
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
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
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 ** )
00765
00766 {
00767 char *pszAuxFilename;
00768
00769
00770
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
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
00800
00801
00802 VSIFWrite( (void *) "\0\0", 2, 1, fp );
00803 VSIFClose( fp );
00804
00805
00806
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
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
00836
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
00849
00850 VSIFPrintf( fp, "RawDefinition: %d %d %d\n",
00851 nXSize, nYSize, nBands );
00852
00853
00854
00855
00856
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
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