/*! \page SDTS_AL_TUT
#include "stds_al.h" ... SDTSTransfer oTransfer; if( !oTransfer.Open( pszCATDFilename ) ) { fprintf( stderr, "Failed to read CATD file `%s'\n", pszCATDFilename ); exit( 100 ); }
In particular, the SDTSTransfer::GetLayerCount() method returns the number of feature layers in the transfer and the SDTSTransfer::GetLayerCATDEntry() is used to translate layer indexes into SDTS_CATD compatible CATD indexes.
printf( "Layers:\n" ); for( i = 0; i < oTransfer.GetLayerCount(); i++ ) { int iCATDEntry = oTransfer.GetLayerCATDEntry(i); printf( " %s: `%s'\n", oTransfer.GetCATD()->GetEntryModule(iCATDEntry), oTransfer.GetCATD()->GetEntryTypeDesc(iCATDEntry) ); } printf( "\n" );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 SDTSTransfer object. The rest are supporting information, much of it, like data quality, is ignored by the SDTS_AL library.
warmerda@cs46980-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 '
The SDTSTransfer::GetLayerIndexedReader() method instantiates a reader of the desired type. In this case we know we are instantiating a SDTSPolygonReader so we can safely cast the returned SDTSIndexedReader pointer to the more specific type SDTSPolygonReader.
SDTSPolygonReader *poPolyReader; poPolyReader = (SDTSPolygonReader *) poTransfer->GetLayerIndexedReader( poTransfer->FindLayer( pszMODN ) ); if( poPolyReader == NULL ) { fprintf( stderr, "Failed to open %s.\n", poTransfer->GetCATD()->GetModuleFilePath( pszMODN ) ); return; }Note that readers returned by SDTSTransfer::GetLayerIndexedReader() are managed by the SDTSTransfer, and should not be deleted by the application.
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.
poPolyReader->AssembleRings( poTransfer );Upon completion the SDTSPolygonReader 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.
In the case of SDTSRawPolygon, 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.
nSDTSRecordField = DBFAddField( hDBF, "SDTSRecId", FTInteger, 8, 0 );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 SDTSIndexedReader::ScanModuleReferences() 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".
char **papszModRefs = poPolyReader->ScanModuleReferences();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.
SDTSAttrReader *poAttrReader; poAttrReader = (SDTSAttrReader *) poTransfer->GetLayerIndexedReader( poTransfer->FindLayer( papszModuleList[iModule] ) ); if( poAttrReader == NULL ) { printf( "Unable to open attribute module %s, skipping.\n" , papszModuleList[iModule] ); continue; } poAttrReader->Rewind();
SDTSAttrRecord *poAttrFeature; poAttrFeature = (SDTSAttrRecord *) poAttrReader->GetNextFeature(); if( poAttrFeature == NULL ) { fprintf( stderr, "Didn't find any meaningful attribute records in %s.\n", papszModuleList[iModule] ); continue; }When no longer needed, the attribute record may need to be explicitly deleted if it is not part of an indexed cached.
if( !poAttrReader->IsIndexed() ) delete poAttrFeature;
DDFFieldDefn *poFDefn = poAttrFeature->poATTR->GetFieldDefn(); int iSF; DDFField *poSR = poAttrFeature->poATTR; for( iSF=0; iSF < poFDefn->GetSubfieldCount(); iSF++ ) { DDFSubfieldDefn *poSFDefn = poFDefn->GetSubfield( iSF );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.
int nWidth = poSFDefn->GetWidth(); switch( poSFDefn->GetType() ) { case DDFString: if( nWidth == 0 ) { int nMaxBytes; const char * pachData = poSR->GetSubfieldData(poSFDefn, &nMaxBytes); nWidth = strlen(poSFDefn->ExtractStringData(pachData, nMaxBytes, NULL )); } DBFAddField( hDBF, poSFDefn->GetName(), FTString, nWidth, 0 ); break; case DDFInt: if( nWidth == 0 ) nWidth = 9; DBFAddField( hDBF, poSFDefn->GetName(), FTInteger, nWidth, 0 ); break; case DDFFloat: DBFAddField( hDBF, poSFDefn->GetName(), FTDouble, 18, 6 ); break; default: fprintf( stderr, "Dropping attribute `%s' of module `%s'. " "Type unsupported\n", poSFDefn->GetName(), papszModuleList[iModule] ); break; } }
SDTSRawPolygon *poRawPoly; poPolyReader->Rewind(); while( (poRawPoly = (SDTSRawPolygon *) poPolyReader->GetNextFeature()) != NULL ) { ... process and write polygon ... if( !poPolyReader->IsIndexed() ) delete poRawPoly; }
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 SDTSRawPolygon. See the SDTSRawPolygon reference help for a fuller definition of the nRings, panRingStart, nVertices, and vertex fields.
psShape = SHPCreateObject( SHPT_POLYGON, -1, poRawPoly->nRings, poRawPoly->panRingStart, NULL, poRawPoly->nVertices, poRawPoly->padfX, poRawPoly->padfY, poRawPoly->padfZ, NULL );
DBFWriteIntegerAttribute( hDBF, iShape, nSDTSRecordField, poRawPoly->oModId.nRecord );
In particular, the poFeature->nAttributes member indicates how many associated attribute records there are. The poFeature->aoATID[] array contains the SDTSModId's for each record. This SDTSModId can be passed to SDTSTransfer::GetAttr() to fetch the DDFField pointer for the user attributes. The WriteAttrRecordToDBF() method is specific to sdts2shp and will be define later.
int iAttrRecord; for( iAttrRecord = 0; iAttrRecord < poFeature->nAttributes; iAttrRecord++) { DDFField *poSR; poSR = poTransfer->GetAttr( poFeature->aoATID+iAttrRecord ); WriteAttrRecordToDBF( hDBF, iRecord, poTransfer, poSR ); }
/* -------------------------------------------------------------------- */ /* Process each subfield in the record. */ /* -------------------------------------------------------------------- */ DDFFieldDefn *poFDefn = poSR->GetFieldDefn(); for( int iSF=0; iSF < poFDefn->GetSubfieldCount(); iSF++ ) { DDFSubfieldDefn *poSFDefn = poFDefn->GetSubfield( iSF ); int iField; int nMaxBytes; const char * pachData = poSR->GetSubfieldData(poSFDefn, &nMaxBytes); /* -------------------------------------------------------------------- */ /* Identify the related DBF field, if any. */ /* -------------------------------------------------------------------- */ for( iField = 0; iField < hDBF->nFields; iField++ ) { if( EQUALN(poSFDefn->GetName(), hDBF->pszHeader+iField*32,10) ) break; } if( iField == hDBF->nFields ) iField = -1; /* -------------------------------------------------------------------- */ /* Handle each of the types. */ /* -------------------------------------------------------------------- */ switch( poSFDefn->GetType() ) { case DDFString: const char *pszValue; pszValue = poSFDefn->ExtractStringData(pachData, nMaxBytes, NULL); if( iField != -1 ) DBFWriteStringAttribute(hDBF, iRecord, iField, pszValue ); break; case DDFFloat: double dfValue; dfValue = poSFDefn->ExtractFloatData(pachData, nMaxBytes, NULL); if( iField != -1 ) DBFWriteDoubleAttribute( hDBF, iRecord, iField, dfValue ); break; case DDFInt: int nValue; nValue = poSFDefn->ExtractIntData(pachData, nMaxBytes, NULL); if( iField != -1 ) DBFWriteIntegerAttribute( hDBF, iRecord, iField, nValue ); break; default: break; } } /* next subfield */
*/ /*! \page sdts2shp.cpp