EVOLUTION-MANAGER
Edit File: vb6_tutorial.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/> <meta http-equiv="X-UA-Compatible" content="IE=9"/> <meta name="generator" content="Doxygen 1.8.5"/> <title>My Project: GDAL VB6 Bindings Tutorial</title> <link href="tabs.css" rel="stylesheet" type="text/css"/> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="dynsections.js"></script> <link href="search/search.css" rel="stylesheet" type="text/css"/> <script type="text/javascript" src="search/search.js"></script> <script type="text/javascript"> $(document).ready(function() { searchBox.OnSelectItem(0); }); </script> <link href="doxygen.css" rel="stylesheet" type="text/css" /> </head> <body> <div id="top"><!-- do not remove this div, it is closed by doxygen! --> <div id="titlearea"> <table cellspacing="0" cellpadding="0"> <tbody> <tr style="height: 56px;"> <td style="padding-left: 0.5em;"> <div id="projectname">My Project </div> </td> </tr> </tbody> </table> </div> <!-- end header part --> <!-- Generated by Doxygen 1.8.5 --> <script type="text/javascript"> var searchBox = new SearchBox("searchBox", "search",false,'Search'); </script> <div id="navrow1" class="tabs"> <ul class="tablist"> <li><a href="index.html"><span>Main Page</span></a></li> <li class="current"><a href="pages.html"><span>Related Pages</span></a></li> <li> <div id="MSearchBox" class="MSearchBoxInactive"> <span class="left"> <img id="MSearchSelect" src="search/mag_sel.png" onmouseover="return searchBox.OnSearchSelectShow()" onmouseout="return searchBox.OnSearchSelectHide()" alt=""/> <input type="text" id="MSearchField" value="Search" accesskey="S" onfocus="searchBox.OnSearchFieldFocus(true)" onblur="searchBox.OnSearchFieldFocus(false)" onkeyup="searchBox.OnSearchFieldChange(event)"/> </span><span class="right"> <a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a> </span> </div> </li> </ul> </div> <!-- window showing the filter options --> <div id="MSearchSelectWindow" onmouseover="return searchBox.OnSearchSelectShow()" onmouseout="return searchBox.OnSearchSelectHide()" onkeydown="return searchBox.OnSearchSelectKey(event)"> <a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(0)"><span class="SelectionMark"> </span>All</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(1)"><span class="SelectionMark"> </span>Pages</a></div> <!-- iframe showing the search results (closed by default) --> <div id="MSearchResultsWindow"> <iframe src="javascript:void(0)" frameborder="0" name="MSearchResults" id="MSearchResults"> </iframe> </div> </div><!-- top --> <div class="header"> <div class="headertitle"> <div class="title">GDAL VB6 Bindings Tutorial </div> </div> </div><!--header--> <div class="contents"> <div class="textblock"><h1><a class="anchor" id="Intro"></a> Introduction</h1> <p>A partial set of Visual Basic 6 bindings have been build for GDAL. Internally these bindings use Declare based calls into the GDAL DLL C API but a set of shadow classes are also provided to provide object oriented access to GDAL services in VB6 similar to those provided in C++.</p> <p>Note that the VB6 bindings are nowhere near comprehensive, nor are they documented. However, in combination with the corresponding C++ class documentation, and the following docs, it should be possible to use GDAL to accomplish a variety of operations. It is not believed that the VB6 bindings will be of any utility with earlier version of VB nor with VB.Net.</p> <p>The classes for which access has been implemented includes GDALDriver, GDALDataset, GDALRasterBand, GDALColorTable, OGRSpatialReference and OGRCoordinateTransformation.</p> <p>A mailing list specifically on VB6 GDAL topics has been setup at <a href="http://groups.yahoo.com/group/gdal-vb6-appdev">http://groups.yahoo.com/group/gdal-vb6-appdev</a> .</p> <h1><a class="anchor" id="UsingVB6"></a> Using GDAL VB6 Classes</h1> <p>To use VB6 GDAL bindings it is necessary to ensure that GDAL has been built with appropriate C entry points exported using the "stdcall" calling convention. This is the current default, but was not as recently as GDAL 1.2.6. So ensure you get a version more recent than 1.2.6.</p> <p>Then add the GDAL VB6 class and module files to your VB6 project. These come from the <a href="http://svn.osgeo.org/gdal/trunk/gdal/vb6">gdal/vb6 directory</a> and include the following key files:</p> <ul> <li> GDAL.bas - The main user visible module. </li> <li> GDALCore.bas - This module is for internal use. </li> <li> GDALDriver.cls - The GDALDriver class. </li> <li> GDALDataset.cls - The GDALDataset class. </li> <li> GDALRasterBand.cls - The GDALRasterBand class. </li> <li> GDALColorTable.cls - The GDALColorTable class. </li> <li> OGRSpatialReference.cls - The OGRSpatialReference class. </li> <li> OGRCoordinateTransformation.cls - The OGRCoordinateTransformation class. </li> </ul> <p>You may need to edit GDALCore.bas, and change occurrences of gdal12.dll to match what your GDAL DLL is called. You can include a full path to the DLL if it can't be guaranteed to be in the current working directory of the application (or the windows system32 directory).</p> <p>You should also be able to load the "test" project from the gdal\vb6\test directory. The test project has test menu items roughly corresponding to the tasks in the following tutorial topics.</p> <h1><a class="anchor" id="TutorialRead"></a> Tutorial - Read Dataset</h1> <p>This brief tutorial will demonstrate open a GDAL file, and fetching out some information, about the dataset, and the individual bands. The results are printed to the default from in the following example for simplicity.</p> <p>Before opening the file we need to register the GDAL format drivers. Normally we will just register all the drivers with GDALAllRegister().</p> <div class="fragment"><div class="line">Call GDAL.AllRegister()</div> </div><!-- fragment --><p>Then we need to try and open the dataset. The GDAL.OpenDS() function returns a GDALDataset object, so we dimension an appropriate object for this. GDAL.OpenDS() is the VB6 equivalent of the GDALDataset::GDALOpen() function.</p> <div class="fragment"><div class="line">Dim ds As GDALDataset</div> <div class="line"></div> <div class="line">Set ds = GDAL.OpenDS( <span class="stringliteral">"utm.tif"</span>, GDAL.GA_ReadOnly )</div> </div><!-- fragment --><p>Then we need to check if the open succeeded, and if not report an error.</p> <div class="fragment"><div class="line">If not ds.IsValid() Then</div> <div class="line"> Call MsgBox( <span class="stringliteral">"Open failed: "</span> & GDAL.GetLastErrorMsg() )</div> <div class="line"> Exit Sub</div> <div class="line">End If</div> </div><!-- fragment --><p>If things succeeded, we query width of the image in pixels (XSize), Height of the image in pixels (YSize) and number of bands (BandCount) from the dataset properties.</p> <div class="fragment"><div class="line">Print <span class="stringliteral">"Size: "</span> & ds.XSize & <span class="stringliteral">"x"</span> & ds.YSize & <span class="stringliteral">"x"</span> & ds.BandCount</div> </div><!-- fragment --><p>Next we read metadata from the dataset using the VB6 equivalent of the GDALMajorObject::GetMetadata() method, and report it to the user. Metadata is returned as an array of strings of "name=value" items. Array indices start at zero in the returned array. The domain argument should normally be vbNullString though in specialized circumstances other domains might apply.</p> <div class="fragment"><div class="line">Dim MD As Variant</div> <div class="line">MD = ds.GetMetadata(vbNullString)</div> <div class="line">If (UBound(MD) > 0) Then</div> <div class="line"> Print "Metadata:"</div> <div class="line"> For i = 1 To UBound(MD)</div> <div class="line"> Print " " & MD(i)</div> <div class="line"> Next i</div> <div class="line">End If</div> </div><!-- fragment --><p>Parsing the "name=value" strings from GetMetadata() can be a bit of a bother, so if we were looking for specific values we could use GetMetadataItem() and provide a specific item we want to extract. This would extract just the value if it is found, or an empty string otherwise. The GetMetadataItem() is an analog of the C++ GDALMajorObject::GetMetadataItem() method.</p> <div class="fragment"><div class="line">Dim MDValue As String</div> <div class="line"></div> <div class="line">MDValue = ds.GetMetadataItem( <span class="stringliteral">"TIFF_DATETIME"</span>, vbNullString )</div> <div class="line"><span class="keywordflow">if</span> MDValue <> <span class="stringliteral">""</span> Then</div> <div class="line"> Print <span class="stringliteral">"Creation Date: "</span> & MDValue</div> <div class="line">End If</div> </div><!-- fragment --><p>The GDALDataset::GetGeoTransform() method is used to get fetch the affine transformation used to relate pixel/line locations on the image to georeferenced locations in the current coordinate system. In the most common case (image is not rotated or sheared) you can just report the origin (upper left corner) and pixel size from these values. The method returns 0 on success or an error class if it fails, so we only use the return result (placed into the Geotransform array) on success.</p> <div class="fragment"><div class="line">Dim Geotransform(6) As Double</div> <div class="line"></div> <div class="line">If ds.GetGeoTransform( Geotransform ) = 0 Then </div> <div class="line"> If Geotransform(2) = 0 and Geotransform(4) = 0 Then</div> <div class="line"> Print "Origin: " & Geotransform(0) & "," & Geotransform(3)</div> <div class="line"> Print "Pixel Size: " & Geotransform(1) & "x" & (-1 * Geotransform(5))</div> <div class="line"> End If</div> <div class="line">End If</div> </div><!-- fragment --><p>The coordinate system can be fetched using the GDALDataset::GetProjectionRef() analog, GDALDataset.GetProjection(). The returned string is in OpenGIS Well Known Text format. A later example will show how to use an OGRSpatialReference object to reformat the WKT into more readable format and make other use of it.</p> <div class="fragment"><div class="line">Dim WKT As String</div> <div class="line"></div> <div class="line">WKT = ds.GetProjection()</div> <div class="line">If Len(WKT) > 0 Then</div> <div class="line"> Print <span class="stringliteral">"Projection: "</span> & WKT</div> <div class="line">End If</div> </div><!-- fragment --><p>GDALDataset objects have one or more raster bands associated with them. GDALRasterBand objects can have metadata (accessed the same as on the GDALDataset) as well as an array of pixel values, and various specialized metadata items like data type, color interpretation, offset/scale. Here we report a few of the items.</p> <p>First we loop over all the bands, fetching a band object for each band and report the band number, and block size.</p> <div class="fragment"><div class="line">For i = 1 To ds.BandCount</div> <div class="line"> Dim band As GDALRasterBand</div> <div class="line"> </div> <div class="line"> Set band = ds.GetRasterBand(i)</div> <div class="line"> Print <span class="stringliteral">"Band "</span> & i & <span class="stringliteral">" BlockSize: "</span> & band.BlockXSize & <span class="stringliteral">"x"</span> & band.BlockYSize</div> </div><!-- fragment --><p>The GDALRasterBand has a DataType property which has the value returned by the C++ method GDALRasterBand::GetRasterDataType(). The returned value is an integer, but may be compared to the predefined constants GDAL.GDT_Byte, GDAL.GDT_UInt16, GDAL.GDT_Int16, GDAL.GDT_UInt32, GDAL.GDT_Int32, GDAL.GDT_Float32, GDAL.GDT_Float64, GDAL.GDT_CInt16, GDAL.GDT_CInt32, GDAL.GDT_CFloat32 and GDAL.GDT_CFloat64. In this case we use the GDAL.GetDataTypeName() method to convert the data type into a name we can show the user.</p> <div class="fragment"><div class="line">Print <span class="stringliteral">" DataType="</span> & GDAL.GetDataTypeName(band.DataType) _</div> </div><!-- fragment --><p>We also report the offset, scale, minimum and maximum for the band.</p> <div class="fragment"><div class="line">Print <span class="stringliteral">" Offset="</span> & band.GetOffset() & <span class="stringliteral">" Scale="</span> & band.GetScale() _</div> <div class="line"> & <span class="stringliteral">" Min="</span> & band.GetMinimum() & <span class="stringliteral">" Max="</span> & band.GetMaximum()</div> </div><!-- fragment --><p>GDALRasterBands can also have GDALColorTable objects associated with them. They are read with the GDALRasterBand::GetColorTable() analog in VB6. Individual RGBA entries should be read into a 4 Integer array.</p> <div class="fragment"><div class="line">Dim ct As GDALColorTable</div> <div class="line">Set ct = band.GetColorTable()</div> <div class="line">If ct.IsValid() Then</div> <div class="line"> Dim CEntry(4) As Integer</div> <div class="line"> Print " Has Color Table, " & ct.EntryCount & " entries"</div> <div class="line"> For iColor = 0 To ct.EntryCount - 1</div> <div class="line"> Call ct.GetColorEntryAsRGB(iColor, CEntry)</div> <div class="line"> Print " " & iColor & ": " & CEntry(0) & "," & CEntry(1) & "," & CEntry(2) & "," & CEntry(3)</div> <div class="line"> Next iColor</div> <div class="line">End If</div> </div><!-- fragment --><p>But of course, the most important contents of a GDAL file is the raster pixel values themselves. The C++ GDALRasterBand::RasterIO() method is provided in a somewhat simplified form. A predimensioned 1D or 2D array of type Byte, Int, Long, Float or Double is passed to the RasterIO() method along with the band and window to be read. Internally the "buffer size" and datatype is extracted from the dimensions of the passed in buffer.</p> <p>This example dimensions the RawData array to be the size of one scanline of data (XSize x 1) and reads the first whole scanline of data from the file, but only prints out the second and tenth values (since the buffer indexes are zero based).</p> <div class="fragment"><div class="line">Dim err As Long</div> <div class="line">Dim RawData() As Double</div> <div class="line">ReDim RawData(ds.XSize) As Double</div> <div class="line"></div> <div class="line">err = band.RasterIO(GDAL.GF_Read, 0, 0, ds.XSize, 1, RawData)</div> <div class="line">if err = 0 Then</div> <div class="line"> Print " Data: " & RawData(1) & " " & RawData(9)</div> <div class="line">End If</div> </div><!-- fragment --><p>Finally, when done accessing a GDALDataset we can explicitly close it using the CloseDS() method, or just let it fall out of scope in which case it will be closed automatically.</p> <div class="fragment"><div class="line">Call ds.CloseDS()</div> </div><!-- fragment --><h1><a class="anchor" id="TutorialCreate"></a> Tutorial - Creating Files</h1> <p>Next we address creating a new file from an existing file. To create a new file, you have to select a GDALDriver to do the creating. The GDALDriver is essentially an object representing a file format. We fetch it with the GetDriverByName() call from the GDAL module using the driver name.</p> <div class="fragment"><div class="line">Dim Drv As GDALDriver</div> <div class="line"></div> <div class="line">Call GDAL.AllRegister</div> <div class="line">Drv = GDALCore.GetDriverByName( <span class="stringliteral">"GTiff"</span> )</div> <div class="line">If Not Drv.IsValid() Then</div> <div class="line"> Call MsgBox( <span class="stringliteral">"GTiff driver not found "</span> )</div> <div class="line">Exit Sub</div> <div class="line">End If</div> </div><!-- fragment --><p>You could get a list of registered drivers, and identify which support creation something like this:</p> <div class="fragment"><div class="line">drvCount = GDAL.GetDriverCount</div> <div class="line">For drvIndex = 0 To drvCount - 1</div> <div class="line"> Set Drv = GDAL.GetDriver(drvIndex)</div> <div class="line"> If Drv.GetMetadataItem(GDAL.DCAP_CREATE, <span class="stringliteral">""</span>) = <span class="stringliteral">"YES"</span> _</div> <div class="line"> Or Drv.GetMetadataItem(GDAL.DCAP_CREATECOPY, <span class="stringliteral">""</span>) = <span class="stringliteral">"YES"</span> Then</div> <div class="line"> xMsg = <span class="stringliteral">" (Read/Write)"</span></div> <div class="line"> Else</div> <div class="line"> xMsg = <span class="stringliteral">" (ReadOnly)"</span></div> <div class="line"> End If</div> <div class="line"> </div> <div class="line"> Print Drv.GetShortName() & <span class="stringliteral">": "</span> & Drv.GetMetadataItem(GDAL.DMD_LONGNAME, <span class="stringliteral">""</span>) & xMsg</div> <div class="line">Next drvIndex</div> </div><!-- fragment --><p>Once we have the driver object, the simplest way of creating a new file is to use CreateCopy(). This tries to create a copy of the input file in the new format. A complete segment (without any error checking) would look like the following. The CreateCopy() method corresponds to the C++ method GDALDriver::CreateCopy(). The VB6 implementation does not support the use of progress callbacks.</p> <div class="fragment"><div class="line">Dim Drv As GDALDriver</div> <div class="line">Dim SrcDS As GDALDataset, DstDS As GDALDataset</div> <div class="line"></div> <div class="line">Call GDAL.AllRegister</div> <div class="line">Set Drv = GDALCore.GetDriverByName( <span class="stringliteral">"GTiff"</span> )</div> <div class="line"></div> <div class="line">Set SrcDS = GDAL.Open( <span class="stringliteral">"in.tif"</span>, GDAL.GA_ReadOnly )</div> <div class="line">Set DstDS = Drv.CreateCopy( <span class="stringliteral">"out.tif"</span>, SrcDS, True, Nothing )</div> </div><!-- fragment --><p>This is nice and simple, but sometimes we need to create a file with more detailed control. So, next we show how to create a file and then copy pieces of data to it "manually". The GDALDriver::Create() analog is Create().</p> <div class="fragment"><div class="line">Set DstDS = Drv.Create(<span class="stringliteral">"out.tif"</span>, SrcDS.XSize, SrcDS.YSize, _</div> <div class="line"> SrcDS.BandCount, GDAL.GDT_Byte, Nothing)</div> </div><!-- fragment --><p>In some cases we may want to provide some creation options, which is demonstrated here. Creation options (like metadata set through the SetMetadata() method) are arrays of Strings.</p> <div class="fragment"><div class="line">Dim CreateOptions(1) As String</div> <div class="line"></div> <div class="line">CreateOptions(1) = "PHOTOMETRIC=MINISWHITE"</div> <div class="line">Set DstDS = Drv.Create("out.tif", SrcDS.XSize, SrcDS.YSize, _</div> <div class="line"> SrcDS.BandCount, GDAL.GDT_Byte, CreateOptions)</div> </div><!-- fragment --><p>When copying the GeoTransform, we take care to check that reading the geotransform actually worked. Most methods which return CPLErr in C++ also return it in VB6. A return value of 0 will indicate success, and non-zero is failure.</p> <div class="fragment"><div class="line">Dim err As Long</div> <div class="line">Dim gt(6) As Double</div> <div class="line"></div> <div class="line">err = SrcDS.GetGeoTransform(gt)</div> <div class="line">If err = 0 Then</div> <div class="line"> Call DstDS.SetGeoTransform(gt)</div> <div class="line">End If</div> </div><!-- fragment --><p>Copy the projection. Even if GetProjection() fails we get an empty string which is safe enough to set on the target. Similarly for metadata.</p> <div class="fragment"><div class="line">Call DstDS.SetProjection(SrcDS.GetProjection())</div> <div class="line">Call DstDS.SetMetadata(SrcDS.GetMetadata(<span class="stringliteral">""</span>), <span class="stringliteral">""</span>)</div> </div><!-- fragment --><p>Next we loop, processing bands, and copy some common data items.</p> <div class="fragment"><div class="line">For iBand = 1 To SrcDS.BandCount</div> <div class="line"> Dim SrcBand As GDALRasterBand, DstBand As GDALRasterBand</div> <div class="line"> </div> <div class="line"> Set SrcBand = SrcDS.GetRasterBand(iBand)</div> <div class="line"> Set DstBand = DstDS.GetRasterBand(iBand)</div> <div class="line"> </div> <div class="line"> Call DstBand.SetMetadata(SrcBand.GetMetadata(<span class="stringliteral">""</span>), <span class="stringliteral">""</span>)</div> <div class="line"> Call DstBand.SetOffset(SrcBand.GetOffset())</div> <div class="line"> Call DstBand.SetScale(SrcBand.GetScale())</div> <div class="line"></div> <div class="line"> Dim NoDataValue As Double, Success As Long</div> <div class="line"> </div> <div class="line"> NoDataValue = SrcBand.GetNoDataValue(Success)</div> <div class="line"> If Success <> 0 Then</div> <div class="line"> Call DstBand.SetNoDataValue(NoDataValue)</div> <div class="line"> End If</div> </div><!-- fragment --><p>Then, if one is available, we copy the palette.</p> <div class="fragment"><div class="line">Dim ct As GDALColorTable</div> <div class="line">Set ct = SrcBand.GetColorTable()</div> <div class="line">If ct.IsValid() Then</div> <div class="line"> err = DstBand.SetColorTable(ct)</div> <div class="line">End If</div> </div><!-- fragment --><p>Finally, the meat and potatoes. We copy the image data. We do this one scanline at a time so that we can support very large images without require large amounts of RAM. Here we use a Double buffer for the scanline, but if we knew in advance the type of the image, we could dimension a buffer of the appropriate type. The RasterIO() method internally knows how to convert pixel data types, so using Double ensures all data types (except for complex) are properly preserved, though at the cost of some extra data conversion internally.</p> <div class="fragment"><div class="line">Dim Scanline() As Double, iLine As Long</div> <div class="line">ReDim Scanline(SrcDS.XSize) As Double</div> <div class="line"></div> <div class="line">' Copy band raster data.</div> <div class="line">For iLine = 0 To SrcDS.YSize - 1</div> <div class="line"> Call SrcBand.RasterIO(GDAL.GF_Read, 0, iLine, SrcDS.XSize, 1, _</div> <div class="line"> Scanline)</div> <div class="line"> Call DstBand.RasterIO(GDAL.GF_Write, 0, iLine, SrcDS.XSize, 1, _</div> <div class="line"> Scanline)</div> <div class="line">Next iLine</div> </div><!-- fragment --><h1><a class="anchor" id="OSRTut"></a> Tutorial - Coordinate Systems and Reprojection</h1> <p>The GDAL VB6 bindings also include limited support for use of the OGRSpatialReference and OGRCoordinateTransformation classes. The OGRSpatialReference represents a coordinate system and can be used to parse, manipulate and form WKT strings, such as those returned by the GDALDataset.GetProjection() method. The OGRCoordinateTransformation class provides a way of reprojecting between two coordinate systems.</p> <p>The following example shows how to report the corners of an image in georeferenced and geographic (lat/long) coordinates. First, we open the file, and read the geotransform.</p> <div class="fragment"><div class="line">Dim ds As GDALDataset</div> <div class="line"></div> <div class="line">Call GDALCore.GDALAllRegister</div> <div class="line">Set ds = GDAL.OpenDS(FileDlg.Filename, GDAL.GA_ReadOnly)</div> <div class="line"></div> <div class="line">If ds.IsValid() Then</div> <div class="line"> Dim Geotransform(6) As Double</div> <div class="line"> </div> <div class="line"> Call ds.GetGeoTransform(Geotransform)</div> </div><!-- fragment --><p>Next, we fetch the coordinate system, and if it is non-empty we try to instantiate an OGRSpatialReference from it.</p> <div class="fragment"><div class="line"><span class="stringliteral">' report projection in pretty format.</span></div> <div class="line"><span class="stringliteral">Dim WKT As String</span></div> <div class="line"><span class="stringliteral">Dim srs As New OGRSpatialReference</span></div> <div class="line"><span class="stringliteral">Dim latlong_srs As OGRSpatialReference</span></div> <div class="line"><span class="stringliteral">Dim ct As New OGRCoordinateTransformation</span></div> <div class="line"><span class="stringliteral"></span></div> <div class="line"><span class="stringliteral">WKT = ds.GetProjection()</span></div> <div class="line"><span class="stringliteral">If Len(WKT) > 0 Then</span></div> <div class="line"><span class="stringliteral"> Print "Projection: "</span></div> <div class="line"><span class="stringliteral"> Call srs.SetFromUserInput(WKT)</span></div> </div><!-- fragment --><p>If the coordinate system is projected it will have a PROJECTION node. In that case we build a new coordinate system which is the corresponding geographic coordinate system. So for instance if the "srs" was UTM 11 WGS84 then it's corresponding geographic coordinate system would just be WGS84. Once we have these two coordinate systems, we build a transformer to convert between them.</p> <div class="fragment"><div class="line"> If srs.GetAttrValue(<span class="stringliteral">"PROJECTION"</span>, 0) <> <span class="stringliteral">""</span> Then</div> <div class="line"> Set latlong_srs = srs.CloneGeogCS()</div> <div class="line"> Set ct = GDAL.CreateCoordinateTransformation(srs, latlong_srs)</div> <div class="line"> End If</div> <div class="line">End If</div> </div><!-- fragment --><p>Next we call a helper function to report each corner, and the center. We pass in the name of the corner, the pixel/line location at the corner, and the geotransform and transformer object.</p> <div class="fragment"><div class="line">Call ReportCorner(<span class="stringliteral">"Top Left "</span>, 0, 0, _</div> <div class="line"> Geotransform, ct)</div> <div class="line">Call ReportCorner("Top Right ", ds.XSize, 0, _</div> <div class="line"> Geotransform, ct)</div> <div class="line">Call ReportCorner("Bottom Left ", 0, ds.YSize, _</div> <div class="line"> Geotransform, ct)</div> <div class="line">Call ReportCorner("Bottom Right ", ds.XSize, ds.YSize, _</div> <div class="line"> Geotransform, ct)</div> <div class="line">Call ReportCorner("Center ", ds.XSize / 2<span class="preprocessor">#, ds.YSize / 2#, _</span></div> <div class="line"><span class="preprocessor"> Geotransform, ct)</span></div> </div><!-- fragment --><p>The ReportCorner subroutine starts by computing the corresponding georeferenced x and y location using the pixel/line coordinates and the geotransform.</p> <div class="fragment"><div class="line">Private Sub ReportCorner(CornerName As String, pixel As Double, line As Double, _</div> <div class="line"> gt() As Double, ct As OGRCoordinateTransformation)</div> <div class="line"> </div> <div class="line"> Dim geox As Double, geoy As Double</div> <div class="line"> </div> <div class="line"> geox = gt(0) + pixel * gt(1) + line * gt(2)</div> <div class="line"> geoy = gt(3) + pixel * gt(4) + line * gt(5)</div> </div><!-- fragment --><p>Next, if we have a transformer, we use it to compute a corresponding latitude and longitude.</p> <div class="fragment"><div class="line">Dim longitude As Double, latitude As Double, Z As Double</div> <div class="line">Dim latlong_valid As Boolean</div> <div class="line"></div> <div class="line">latlong_valid = False</div> <div class="line"></div> <div class="line">If ct.IsValid() Then</div> <div class="line"> Z = 0</div> <div class="line"> longitude = geox</div> <div class="line"> latitude = geoy</div> <div class="line"> latlong_valid = ct.TransformOne(longitude, latitude, Z)</div> <div class="line">End If</div> </div><!-- fragment --><p>Then we report the corner location in georeferenced, and if we have it geographic coordinates.</p> <div class="fragment"><div class="line"> If latlong_valid Then</div> <div class="line"> Print CornerName & geox & <span class="stringliteral">","</span> & geoy & <span class="stringliteral">" "</span> & longitude & <span class="stringliteral">","</span> & latitude</div> <div class="line"> Else</div> <div class="line"> Print CornerName & geox & <span class="stringliteral">","</span> & geoy</div> <div class="line"> End If</div> <div class="line">End Sub</div> </div><!-- fragment --> <p> $Id: vb6_tutorial.dox 13528 2008-01-14 19:37:42Z warmerdam $ </p> </div></div><!-- contents --> <!-- start footer part --> <hr class="footer"/><address class="footer"><small> Generated by  <a href="http://www.doxygen.org/index.html"> <img class="footer" src="doxygen.png" alt="doxygen"/> </a> 1.8.5 </small></address> </body> </html>