EVOLUTION-MANAGER
Edit File: SDTS_AL_TUT.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>SDTS_AL: $title</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="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">SDTS_AL </div> </td> </tr> </tbody> </table> </div> <!-- end header part --> <!-- Generated by Doxygen 1.8.5 --> <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><a href="annotated.html"><span>Classes</span></a></li> <li><a href="files.html"><span>Files</span></a></li> </ul> </div> </div><!-- top --> <div class="contents"> <div class="textblock"><center> <title>SDTS Abstraction Library Tutorial</title> </center><p>This page is a walk through of the polygon layer portion of the <a href="sdts2shp.cpp.html">sdts2shp.cpp</a> example application. It is should give sufficient information to utilize the SDTS_AL library to read SDTS files.</p> <h2>Opening the Transfer</h2> <p>The following statements will open an SDTS transfer. The filename passed to <a class="el" href="classSDTSTransfer.html#a6aa55051a8c6baa8c24764f0d5e7c279">SDTSTransfer::Open()</a> should be the name of the catalog file, such as <code>palo_alto/SC01CATD.DDF</code>. The Open() method returns FALSE if it fails for any reason. In addition to the message we print out ourselves, the <a class="el" href="classSDTSTransfer.html#a6aa55051a8c6baa8c24764f0d5e7c279">SDTSTransfer::Open()</a> method will also emit it's own error message using CPLError(). See the <a class="el" href="cpl__error_8h.html">cpl_error.h</a> page for more information on how to capture and control CPLError() style error reporting.</p> <pre> #include "stds_al.h"</pre><pre>...</pre><pre> <a class="el" href="classSDTSTransfer.html">SDTSTransfer</a> oTransfer;</pre><pre> if( !oTransfer.Open( pszCATDFilename ) ) { fprintf( stderr, "Failed to read CATD file `%s'\n", pszCATDFilename ); exit( 100 ); } </pre><h2>Getting a Layer List</h2> <p>Once an <a class="el" href="classSDTSTransfer.html">SDTSTransfer</a> has been opened, it is possible to establish what layers are available. The sdts2shp example problem includes a -v argument to dump a list of available layers. It isn't normally necessary to use the <a class="el" href="classSDTS__CATD.html">SDTS_CATD</a> (catalog) from an application to access SDTS files; however, in this example we use it to fetch a module name, and description for each of the available layers.</p> <p>In particular, the SDTSTransfer::GetLayerCount() method returns the number of feature layers in the transfer and the <a class="el" href="classSDTSTransfer.html#a8a161e795535c6baae7583535f9da7c8">SDTSTransfer::GetLayerCATDEntry()</a> is used to translate layer indexes into <a class="el" href="classSDTS__CATD.html">SDTS_CATD</a> compatible CATD indexes.</p> <pre> printf( "Layers:\n" ); for( i = 0; i < oTransfer.GetLayerCount(); i++ ) { int iCATDEntry = oTransfer.GetLayerCATDEntry(i);</pre><pre> printf( " %s: `%s'\n", oTransfer.GetCATD()->GetEntryModule(iCATDEntry), oTransfer.GetCATD()->GetEntryTypeDesc(iCATDEntry) ); } printf( "\n" ); </pre><p>The following would be a typical layer list. Note that there are many other modules (files) registered with the catalog, but only these ones are considered to be feature layers by the <a class="el" href="classSDTSTransfer.html">SDTSTransfer</a> object. The rest are supporting information, much of it, like data quality, is ignored by the SDTS_AL library.</p> <pre> warmerda-c[113]% sdts2shp data/SC01CATD.DDF -v Layers: ASCF: `Attribute Primary ' AHDR: `Attribute Primary ' NP01: `Point-Node ' NA01: `Point-Node ' NO01: `Point-Node ' LE01: `Line ' PC01: `Polygon ' </pre><h2>Getting a Reader</h2> <p>In order to read polygon features, it is necessary to instantiate a polygon reader on the desired layer. The sdts2shp.cpp program allow the user to select a module name (such as PC01, stored in pszMODN) to write to shape format. Other application might just search for, and operate on all known layers of a desired type.</p> <p>The <a class="el" href="classSDTSTransfer.html#a028d3ec2c7b3bcc719137e632b907ca3">SDTSTransfer::GetLayerIndexedReader()</a> method instantiates a reader of the desired type. In this case we know we are instantiating a <a class="el" href="classSDTSPolygonReader.html">SDTSPolygonReader</a> so we can safely cast the returned <a class="el" href="classSDTSIndexedReader.html">SDTSIndexedReader</a> pointer to the more specific type <a class="el" href="classSDTSPolygonReader.html">SDTSPolygonReader</a>.</p> <pre> <a class="el" href="classSDTSPolygonReader.html">SDTSPolygonReader</a> *poPolyReader;</pre><pre> poPolyReader = (<a class="el" href="classSDTSPolygonReader.html">SDTSPolygonReader</a> *) poTransfer->GetLayerIndexedReader( poTransfer->FindLayer( pszMODN ) );</pre><pre> if( poPolyReader == NULL ) { fprintf( stderr, "Failed to open %s.\n", poTransfer->GetCATD()->GetModuleFilePath( pszMODN ) ); return; } </pre><p>Note that readers returned by <a class="el" href="classSDTSTransfer.html#a028d3ec2c7b3bcc719137e632b907ca3">SDTSTransfer::GetLayerIndexedReader()</a> are managed by the <a class="el" href="classSDTSTransfer.html">SDTSTransfer</a>, and should not be deleted by the application.</p> <h2>Collecting Polygon Geometry</h2> <p>The SDTS TVP format does not directly associate a polygons geometry (the points forming it's boundary) with the polygon feature. Instead it is stored in separate line layers, and the lines contain references to the right, and left polygons that the lines border.</p> <p>The SDTS_AL library provides a convenient method for forming the polygon geometry. Basically just call the SDTSPolygonReader::AssemblePolygons() method. This method will scan all SLTLine layers in the transfer, indexing them and attaching their line work to the polygons. Then it assembles the line work into rings. It also ensures that the outer ring comes first, that the outer ring is counter-clockwise and that the inner ring(s) are clockwise.</p> <pre> poPolyReader->AssembleRings( poTransfer ); </pre><p>Upon completion the <a class="el" href="classSDTSPolygonReader.html">SDTSPolygonReader</a> will have been "indexed". That means that all the polygon information will have been read from disk, and the polygon objects will now have information stored with them indicating the list of edges that form their border.</p> <h2>Identifying Attributes</h2> <p>In order to create the schema for the output shapefile dataset, it is necessary to identify the attributes associated with the polygons. There are two types of attributes which can occur. The first are hardcoded attributes specific to the feature type, and the second are generic user attributes stored in a separate primary attribute layer.</p> <p>In the case of <a class="el" href="classSDTSRawPolygon.html">SDTSRawPolygon</a>, there is only one attribute of interest, and that is the record number of the polygon. This is actually stored within the oModId data member of the SDTSIndexedFeature base class, as will be seen in later examples when we write it to disk. For now we create a DBF field for the record number. This record number is a unique identifier of the polygon within this module/layer.</p> <pre> nSDTSRecordField = DBFAddField( hDBF, "SDTSRecId", FTInteger, 8, 0 ); </pre><p>Identification of user attributes is more complicated. Any feature in a layer can have associates with 0, 1, 2 or potentially more attribute records in other primary attribute layers. In order to establish a schema for the layer it is necessary to build up a list of all attribute layers (tables) to which references appear. The <a class="el" href="classSDTSIndexedReader.html#a4bc517f41916afab133cfadc01568b12">SDTSIndexedReader::ScanModuleReferences()</a> method can be used to scan a whole module for references to attribute modules via the ATID field. The return result is a list of referenced modules in the form of a string list. In a typical case this is one or two modules, such as "ASCF".</p> <pre> char **papszModRefs = poPolyReader->ScanModuleReferences(); </pre><p>In sdts2shp.cpp, a subroutine (AddPrimaryAttrToDBFSchema()) is defined to add all the fields of all references attribute layers to the DBF file. For each module in the list the following steps are executed.</p> <h3>Fetch an Attribute Module Reader</h3> <p>The following code is similar to our code for create a polygon layer reader. It creates a reader on one of the attribute layers referenced. We explicitly rewind it since it may have been previously opened and read by another part of the application.</p> <pre> <a class="el" href="classSDTSAttrReader.html">SDTSAttrReader</a> *poAttrReader;</pre><pre> poAttrReader = (<a class="el" href="classSDTSAttrReader.html">SDTSAttrReader</a> *) poTransfer->GetLayerIndexedReader( poTransfer->FindLayer( papszModuleList[iModule] ) );</pre><pre> if( poAttrReader == NULL ) { printf( "Unable to open attribute module %s, skipping.\n" , papszModuleList[iModule] ); continue; }</pre><pre> poAttrReader->Rewind(); </pre><h3>Get a Prototype Record</h3> <p>In order to get access to field definitions, and in order to establish some sort of reasonable default lengths for field without fixed lengths the sdts2shp program fetches a prototype record from the attribute module.</p> <pre> <a class="el" href="classSDTSAttrRecord.html">SDTSAttrRecord</a> *poAttrFeature;</pre><pre> poAttrFeature = (<a class="el" href="classSDTSAttrRecord.html">SDTSAttrRecord</a> *) poAttrReader->GetNextFeature(); if( poAttrFeature == NULL ) { fprintf( stderr, "Didn't find any meaningful attribute records in %s.\n", papszModuleList[iModule] );</pre><pre> continue; } </pre><p>When no longer needed, the attribute record may need to be explicitly deleted if it is not part of an indexed cached.</p> <pre> if( !poAttrReader->IsIndexed() ) delete poAttrFeature; </pre><h3>Extract Field Definitions</h3> <p>The Shapefile DBF fields are defined based on the information available for each of the subfields of the attribute records ATTR <a class="el" href="classDDFField.html">DDFField</a> (the poATTR data member). The following code loops over each of the subfields, getting a pointer to the DDBSubfieldDefn containing information about that subfield.</p> <pre> <a class="el" href="classDDFFieldDefn.html">DDFFieldDefn</a> *poFDefn = poAttrFeature->poATTR->GetFieldDefn(); int iSF; <a class="el" href="classDDFField.html">DDFField</a> *poSR = poAttrFeature->poATTR;</pre><pre> for( iSF=0; iSF < poFDefn->GetSubfieldCount(); iSF++ ) { <a class="el" href="classDDFSubfieldDefn.html">DDFSubfieldDefn</a> *poSFDefn = poFDefn->GetSubfield( iSF ); </pre><p>Then each of the significant ISO8211 field types is translated to an appropriate DBF field type. In cases where the nWidth field is zero, indicating that the field is variable width, we use the length of the field in the prototype record. Ideally we would scan the whole file to find the longest value for each field, but that would be a significant amount of work. </p> <pre> int nWidth = poSFDefn->GetWidth();</pre><pre> switch( poSFDefn->GetType() ) { case DDFString: if( nWidth == 0 ) { int nMaxBytes;</pre><pre> const char * pachData = poSR->GetSubfieldData(poSFDefn, &nMaxBytes);</pre><pre> nWidth = strlen(poSFDefn->ExtractStringData(pachData, nMaxBytes, NULL )); }</pre><pre> DBFAddField( hDBF, poSFDefn->GetName(), FTString, nWidth, 0 ); break;</pre><pre> case DDFInt: if( nWidth == 0 ) nWidth = 9;</pre><pre> DBFAddField( hDBF, poSFDefn->GetName(), FTInteger, nWidth, 0 ); break;</pre><pre> case DDFFloat: DBFAddField( hDBF, poSFDefn->GetName(), FTDouble, 18, 6 ); break;</pre><pre> default: fprintf( stderr, "Dropping attribute `%s' of module `%s'. " "Type unsupported\n", poSFDefn->GetName(), papszModuleList[iModule] ); break; } } </pre><h2>Reading Polygon Features</h2> <p>With definition of the attribute schema out of the way, we return to the main event, reading polygons from the polygon layer. We have already instantiated the <a class="el" href="classSDTSPolygonReader.html">SDTSPolygonReader</a> (poPolyReader), and now we loop reading features from it. Note that we Rewind() the reader to ensure we are starting at the beginning. After we are done process the polygon we delete it, if and only if the layer does not have an index cache.</p> <pre> <a class="el" href="classSDTSRawPolygon.html">SDTSRawPolygon</a> *poRawPoly;</pre><pre> poPolyReader->Rewind(); while( (poRawPoly = (<a class="el" href="classSDTSRawPolygon.html">SDTSRawPolygon</a> *) poPolyReader->GetNextFeature()) != NULL ) { ... process and write polygon ...</pre><pre> if( !poPolyReader->IsIndexed() ) delete poRawPoly; } </pre><h2>Translate Geometry</h2> <p>In an earlier step we used the <a class="el" href="classSDTSPolygonReader.html#aaed64969af21d26a7cf55312de5e5be7">SDTSPolygonReader::AssembleRings()</a> method to build ring geometry on the polygons from the linework in the line layers.</p> <p>Coincidently (well, ok, maybe it isn't a coincidence) it so happens that the ring organization exactly matches what is needed for the shapefile api. The following call creates a polygon from the ring information in the <a class="el" href="classSDTSRawPolygon.html">SDTSRawPolygon</a>. See the <a class="el" href="classSDTSRawPolygon.html">SDTSRawPolygon</a> reference help for a fuller definition of the nRings, panRingStart, nVertices, and vertex fields.</p> <pre> psShape = SHPCreateObject( SHPT_POLYGON, -1, poRawPoly->nRings, poRawPoly->panRingStart, NULL, poRawPoly->nVertices, poRawPoly->padfX, poRawPoly->padfY, poRawPoly->padfZ, NULL ); </pre><h2>Write Record Number</h2> <p>The following call is used to write out the record number of the polygon, fetched from the SDTSIndexedFeature::oModId data member. The szModule value in this data field will always match the module name for the whole layer. While not shown here, there is also an szOBRP field on oModId which have different values depending on whether the polygon is a universe or regular polygon.</p> <pre> DBFWriteIntegerAttribute( hDBF, iShape, nSDTSRecordField, poRawPoly->oModId.nRecord ); </pre><h2>Fetch Associated User Records</h2> <p>In keeping with the setting up of the schema, accessing the user records is somewhat complicated. In sdts2shp, the primary attribute records associated with any feature (including SDTSRawPolygons) can be fetched with the WriteAttrRecordToDBF() function defined as follows.</p> <p>In particular, the poFeature->nAttributes member indicates how many associated attribute records there are. The poFeature->aoATID[] array contains the <a class="el" href="classSDTSModId.html">SDTSModId</a>'s for each record. This <a class="el" href="classSDTSModId.html">SDTSModId</a> can be passed to <a class="el" href="classSDTSTransfer.html#a4eeffce018629c3a27102e213255381f">SDTSTransfer::GetAttr()</a> to fetch the <a class="el" href="classDDFField.html">DDFField</a> pointer for the user attributes. The WriteAttrRecordToDBF() method is specific to sdts2shp and will be define later.</p> <pre> int iAttrRecord;</pre><pre> for( iAttrRecord = 0; iAttrRecord < poFeature->nAttributes; iAttrRecord++) { <a class="el" href="classDDFField.html">DDFField</a> *poSR;</pre><pre> poSR = poTransfer->GetAttr( poFeature->aoATID+iAttrRecord );</pre><pre> WriteAttrRecordToDBF( hDBF, iRecord, poTransfer, poSR ); } </pre><h2>Write User Attributes</h2> <p>In a manner analygous to the definition of the fields from the prototype attribute record, the following code loops over the subfields, and fetches the data for each. The data extraction via poSR->GetSubfieldData() is a bit involved, and more information can be found on the <a class="el" href="classDDFField.html">DDFField</a> reference page.</p> <pre> /* -------------------------------------------------------------------- */ /* Process each subfield in the record. */ /* -------------------------------------------------------------------- */ <a class="el" href="classDDFFieldDefn.html">DDFFieldDefn</a> *poFDefn = poSR->GetFieldDefn();</pre><pre> { <a class="el" href="classDDFSubfieldDefn.html">DDFSubfieldDefn</a> *poSFDefn = poFDefn->GetSubfield( iSF ); int iField; int nMaxBytes; const char * pachData = poSR->GetSubfieldData(poSFDefn, &nMaxBytes);</pre><pre>/* -------------------------------------------------------------------- */ /* Identify the related DBF field, if any. */ /* -------------------------------------------------------------------- */ for( iField = 0; iField < hDBF->nFields; iField++ ) { if( EQUALN(poSFDefn->GetName(), hDBF->pszHeader+iField*32,10) ) break; }</pre><pre> iField = -1;</pre><pre>/* -------------------------------------------------------------------- */ /* Handle each of the types. */ /* -------------------------------------------------------------------- */</pre><pre> { case DDFString: const char *pszValue;</pre><pre> pszValue = poSFDefn->ExtractStringData(pachData, nMaxBytes, NULL);</pre><pre> DBFWriteStringAttribute(hDBF, iRecord, iField, pszValue ); break;</pre><pre> case DDFFloat: double dfValue;</pre><pre> dfValue = poSFDefn->ExtractFloatData(pachData, nMaxBytes, NULL);</pre><pre> DBFWriteDoubleAttribute( hDBF, iRecord, iField, dfValue ); break;</pre><pre> case DDFInt: int nValue;</pre><pre> nValue = poSFDefn->ExtractIntData(pachData, nMaxBytes, NULL);</pre><pre> DBFWriteIntegerAttribute( hDBF, iRecord, iField, nValue ); break;</pre><pre> default: break; } } /* next subfield */ </pre><h2>Cleanup</h2> <p>In the case of sdts2shp, the <a class="el" href="classSDTSTransfer.html">SDTSTransfer</a> is created on the stack. When it falls out of scope it is destroyed, and all the indexed readers, and their indexed features caches are also cleaned up.</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>