📄 gdal_drivertut.html
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1"><title>GDAL: GDAL Driver Implementation Tutorial</title><link href="doxygen.css" rel="stylesheet" type="text/css"><link href="tabs.css" rel="stylesheet" type="text/css"></head><body><!-- Generated by Doxygen 1.5.1 --><div class="tabs"> <ul> <li><a href="index.html"><span>Main Page</span></a></li> <li><a href="annotated.html"><span>Classes</span></a></li> <li><a href="files.html"><span>Files</span></a></li> <li><a href="pages.html"><span>Related Pages</span></a></li> </ul></div><h1><a class="anchor" name="gdal_drivertut">GDAL Driver Implementation Tutorial</a></h1><h2><a class="anchor" name="gdal_drivertut_overall">Overall Approach</a></h2>In general new formats are added to GDAL by implementing format specific drivers as subclasses of <a class="el" href="classGDALDataset.html">GDALDataset</a>, and band accessors as subclasses of <a class="el" href="classGDALRasterBand.html">GDALRasterBand</a>. As well, a <a class="el" href="classGDALDriver.html">GDALDriver</a> instance is created for the format, and registered with the <a class="el" href="classGDALDriverManager.html">GDALDriverManager</a>, to ensure that the system <em>knows</em> about the format.<p>This tutorial will start with implementing a simple read-only driver (based on the JDEM driver), and then proceed to utilizing the RawRasterBand helper class, implementing creatable and updatable formats, and some esoteric issues.<p>It is strongly advised that the <a href="gdal_datamodel.html">GDAL Data Model </a> description be reviewed and understood before attempting to implement a GDAL driver.<h2><a class="anchor" name="gdal_drivertut_toc">Contents</a></h2><ol><li><a class="el" href="gdal_drivertut.html#gdal_drivertut_dataset">Implementing the Dataset</a> </li><li><a class="el" href="gdal_drivertut.html#gdal_drivertut_rasterband">Implementing the RasterBand</a> </li><li><a class="el" href="gdal_drivertut.html#gdal_drivertut_driver">The Driver</a> </li><li><a class="el" href="gdal_drivertut.html#gdal_drivertut_addingdriver">Adding Driver to GDAL Tree</a> </li><li><a class="el" href="gdal_drivertut.html#gdal_drivertut_georef">Adding Georeferencing</a> </li><li><a class="el" href="gdal_drivertut.html#gdal_drivertut_overviews">Overviews</a> </li><li><a class="el" href="gdal_drivertut.html#gdal_drivertut_creation">File Creation</a> </li><li><a class="el" href="gdal_drivertut.html#gdal_drivertut_raw">RawDataset/RawRasterBand Helper Classes</a> </li><li><a class="el" href="gdal_drivertut.html#gdal_drivertut_metadata">Metadata, and Other Exotic Extensions</a> </li></ol><h2><a class="anchor" name="gdal_drivertut_dataset">Implementing the Dataset</a></h2>We will start showing minimal implementation of a read-only driver for the Japanese DEM format (<a href="jdemdataset.cpp.html">jdemdataset.cpp</a>). First we declare a format specific dataset class, JDEMDataset in this case.<p><div class="fragment"><pre class="fragment"><span class="keyword">class </span>JDEMDataset : <span class="keyword">public</span> <a class="code" href="classGDALDataset.html">GDALDataset</a>{ FILE *fp; GByte abyHeader[1012]; <span class="keyword">public</span>: ~JDEMDataset(); <span class="keyword">static</span> <a class="code" href="classGDALDataset.html">GDALDataset</a> *Open( GDALOpenInfo * );};</pre></div><p>In general we provide capabilities for a driver, by overriding the various virtual methods on the <a class="el" href="classGDALDataset.html">GDALDataset</a> base class. However, the Open() method is special. This is not a virtual method on the base class, and we will need a freestanding function for this operation, so we declare it static. Implementing it as a method in the JDEMDataset class is convenient because we have privileged access to modify the contents of the database object.<p>The open method itself may look something like this:<p><div class="fragment"><pre class="fragment">GDALDataset *JDEMDataset::Open( GDALOpenInfo * poOpenInfo ){// -------------------------------------------------------------------- // Before trying JDEMOpen() we first verify that there is at // least one "\n#keyword" type signature in the first chunk of // the file. // -------------------------------------------------------------------- if( poOpenInfo->fp == NULL || poOpenInfo->nHeaderBytes < 50 ) return NULL; // check if century values seem reasonable if( (!EQUALN((char *)poOpenInfo->pabyHeader+11,"19",2) && !EQUALN((char *)poOpenInfo->pabyHeader+11,"20",2)) || (!EQUALN((char *)poOpenInfo->pabyHeader+15,"19",2) && !EQUALN((char *)poOpenInfo->pabyHeader+15,"20",2)) || (!EQUALN((char *)poOpenInfo->pabyHeader+19,"19",2) && !EQUALN((char *)poOpenInfo->pabyHeader+19,"20",2)) ) { return NULL; } // -------------------------------------------------------------------- // Create a corresponding GDALDataset. // -------------------------------------------------------------------- JDEMDataset *poDS; poDS = new JDEMDataset(); poDS->fp = poOpenInfo->fp; poOpenInfo->fp = NULL; // -------------------------------------------------------------------- // Read the header. // -------------------------------------------------------------------- VSIFSeek( poDS->fp, 0, SEEK_SET ); VSIFRead( poDS->abyHeader, 1, 1012, poDS->fp ); poDS->nRasterXSize = JDEMGetField( (char *) poDS->abyHeader + 23, 3 ); poDS->nRasterYSize = JDEMGetField( (char *) poDS->abyHeader + 26, 3 );// -------------------------------------------------------------------- // Create band information objects. // -------------------------------------------------------------------- poDS->nBands = 1; poDS->SetBand( 1, new JDEMRasterBand( poDS, 1 )); return( poDS );}</pre></div><p>The first step in any database Open function is to verify that the file being passed is in fact of the type this driver is for. It is important to realize that each driver's Open function is called in turn till one succeeds. Drivers must quietly return NULL if the passed file is not of their format. They should only produce an error if the file does appear to be of their supported format, but is for some reason unsupported or corrupt.<p>The information on the file to be opened is passed in contained in a GDALOpenInfo object. The GDALOpenInfo includes the following public data members:<p><div class="fragment"><pre class="fragment"> <span class="keywordtype">char</span> *pszFilename; <a class="code" href="gdal_8h.html#045e3967c208993f70257bfd40c9f1d7">GDALAccess</a> eAccess; <span class="comment">// GA_ReadOnly or GA_Update</span> GBool bStatOK; VSIStatBuf sStat; FILE *fp; <span class="keywordtype">int</span> nHeaderBytes; GByte *pabyHeader;</pre></div><p>The driver can inspect these to establish if the file is supported. If the pszFilename refers to an object in the file system, the <b>bStatOK</b> flag will be set, and the <b>sStat</b> structure will contain normal stat() information about the object (be it directory, file, device). If the object is a regular readable file, the <b>fp</b> will be non-NULL, and can be used for reads on the file (please use the VSI stdio functions from <a class="el" href="cpl__vsi_8h.html">cpl_vsi.h</a>). As well, if the file was successfully opened, the first kilobyte or so is read in, and put in <b>pabyHeader</b>, with the exact size in <b>nHeaderBytes</b>.<p>In this typical testing example it is verified that the file was successfully opened, that we have at least enough header information to perform our test, and that various parts of the header are as expected for this format. In this case, there are no <em>magic</em> numbers for JDEM format so we check various date fields to ensure they have reasonable century values. If the test fails, we quietly return NULL indicating this file isn't of our supported format.<p><div class="fragment"><pre class="fragment"> <span class="keywordflow">if</span>( poOpenInfo->fp == NULL || poOpenInfo->nHeaderBytes < 50 ) <span class="keywordflow">return</span> NULL; <span class="comment">// check if century values seem reasonable</span> <span class="keywordflow">if</span>( (!EQUALN((<span class="keywordtype">char</span> *)poOpenInfo->pabyHeader+11,<span class="stringliteral">"19"</span>,2) && !EQUALN((<span class="keywordtype">char</span> *)poOpenInfo->pabyHeader+11,<span class="stringliteral">"20"</span>,2)) || (!EQUALN((<span class="keywordtype">char</span> *)poOpenInfo->pabyHeader+15,<span class="stringliteral">"19"</span>,2) && !EQUALN((<span class="keywordtype">char</span> *)poOpenInfo->pabyHeader+15,<span class="stringliteral">"20"</span>,2)) || (!EQUALN((<span class="keywordtype">char</span> *)poOpenInfo->pabyHeader+19,<span class="stringliteral">"19"</span>,2) && !EQUALN((<span class="keywordtype">char</span> *)poOpenInfo->pabyHeader+19,<span class="stringliteral">"20"</span>,2)) ) { <span class="keywordflow">return</span> NULL; }</pre></div><p>It is important to make the <em>is this my format</em> test as stringent as possible. In this particular case the test is weak, and a file that happened to have 19s or 20s at a few locations could be erroneously recognized as JDEM format, causing it to not be handled properly.<p>Once we are satisfied that the file is of our format, we need to create an instance of the database class in which we will set various information of interest.<p><div class="fragment"><pre class="fragment"> JDEMDataset *poDS; poDS = <span class="keyword">new</span> JDEMDataset(); poDS->fp = poOpenInfo->fp; poOpenInfo->fp = NULL;</pre></div><p>Generally at this point we would open the file, to acquire a file handle for the dataset; however, if read-only access is sufficient it is permitted to <b>assume ownership</b> of the FILE * from the GDALOpenInfo object. Just ensure that it is set to NULL in the GDALOpenInfo to avoid having it get closed twice. It is also important to note that the state of the FILE * adopted is indeterminate. Ensure that the current location is reset with VSIFSeek() before assuming you can read from it. This is accomplished in the following statements which reset the file and read the header.<p><div class="fragment"><pre class="fragment"> VSIFSeek( poDS->fp, 0, SEEK_SET ); VSIFRead( poDS->abyHeader, 1, 1012, poDS->fp );</pre></div><p>Next the X and Y size are extracted from the header. The nRasterXSize and nRasterYSize are data fields inherited from the <a class="el" href="classGDALDataset.html">GDALDataset</a> base class, and must be set by the Open() method.<p><div class="fragment"><pre class="fragment"> poDS->nRasterXSize = JDEMGetField( (<span class="keywordtype">char</span> *) poDS->abyHeader + 23, 3 ); poDS->nRasterYSize = JDEMGetField( (<span class="keywordtype">char</span> *) poDS->abyHeader + 26, 3 );</pre></div><p>Finally, all the bands related to this dataset must be attached using the SetBand() method. We will explore the JDEMRasterBand() class shortly.<p><div class="fragment"><pre class="fragment"> poDS->SetBand( 1, <span class="keyword">new</span> JDEMRasterBand( poDS, 1 )); <span class="keywordflow">return</span>( poDS );</pre></div><h2><a class="anchor" name="gdal_drivertut_rasterband">Implementing the RasterBand</a></h2>Similar to the customized JDEMDataset class subclassed from <a class="el" href="classGDALDataset.html">GDALDataset</a>, we also need to declare and implement a customized JDEMRasterBand derived from <a class="el" href="classGDALRasterBand.html">GDALRasterBand</a> for access to the band(s) of the JDEM file. For JDEMRasterBand the declaration looks like this:<p><div class="fragment"><pre class="fragment"><span class="keyword">class </span>JDEMRasterBand : <span class="keyword">public</span> <a class="code" href="classGDALRasterBand.html">GDALRasterBand</a>{ <span class="keyword">public</span>: JDEMRasterBand( JDEMDataset *, <span class="keywordtype">int</span> ); <span class="keyword">virtual</span> CPLErr IReadBlock( <span class="keywordtype">int</span>, <span class="keywordtype">int</span>, <span class="keywordtype">void</span> * );};</pre></div><p>The constructor may have any signature, and is only called from the Open() method. Other virtual methods, such as IReadBlock() must be exactly matched to the method signature in <a class="el" href="gdal__priv_8h-source.html">gdal_priv.h</a>.<p>The constructor implementation looks like this:<p><div class="fragment"><pre class="fragment">JDEMRasterBand::JDEMRasterBand( JDEMDataset *poDS, <span class="keywordtype">int</span> nBand ){ this->poDS = poDS; this->nBand = nBand; eDataType = <a class="code" href="gdal_8h.html#22e22ce0a55036a96f652765793fb7a4f5cbd2f96abffd9ac061fc0dced5cbba">GDT_Float32</a>; nBlockXSize = poDS->GetRasterXSize(); nBlockYSize = 1;}</pre></div><p>The following data members are inherited from <a class="el" href="classGDALRasterBand.html">GDALRasterBand</a>, and should generally be set in the band constructor.<p><ul><li><b>poDS</b>: Pointer to the parent <a class="el" href="classGDALDataset.html">GDALDataset</a>. </li><li><b>nBand</b>: The band number within the dataset. </li><li><b>eDataType</b>: The data type of pixels in this band. </li><li><b>nBlockXSize</b>: The width of one block in this band. </li><li><b>nBlockYSize</b>: The height of one block in this band. </li></ul>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -