/*! \page sfc_tutorial
Simple Features Client Tutorial

Introduction

The Simple Features Client API is a set of classes, and support functions that simplify access to OpenGIS SF OLE DB providers.

Most of the classes are based on the ATL OLE DB consumer templates. To fully understand the classes it is critical to also review the Microsoft provided information on these classes. It can be accessed in the MSDN Online Library.

In general terms, the OpenGIS SF COM interface specification requires geographic data providers to make their datasets look like a database. A data source (usually a single data file, or group of related data files) can contain one or more spatial tables which roughly correspond to what be called layers in most systems. Each table has a schema defining the set of attributes (table fields) that apply to each feature in that table (layer). Each record in the table corresponds to a features, with a set of attributes, and some geometry. The geometry is stored in one field of the table as a BLOB (binary large object) encoded in a particular format (OpenGIS SF Well Known Binary Format).

The Simple Features Client classes help model the different parts of this data model:

Example Client

The console program SFCDUMP is intended to be an example of how to use the client api (as well as being a useful debugging tool in it's own right). The full source should be available in gdal/ogr/sfcdump.cpp.

SFCEnumerator: Finding a Provider

The SFCEnumerator class is used to identify all the installed OLE DB providers (drivers) installed on a system. It essentially makes the list of OLE DB providers appear to be a table. The following code sequence demonstrates how to create an SFCEnumerator, loop through all the records, and print out some information for each record.

static void SFCDumpProviders()

{
    SFCEnumerator      oEnum;

    printf( "Available OLE DB Providers\n" );
    printf( "==========================\n" );

    if( FAILED(oEnum.Open()) )
    {
        printf( "Failed to initialize SFCEnumerator.\n" );
        return;
    }

    while( oEnum.MoveNext() == S_OK )
    {
        printf( "%S: %S\n", 
                oEnum.m_szName, oEnum.m_szDescription );

        if( oEnum.IsOGISProvider() )
            printf( "    (OGISDataProvider)\n" );

        printf( "\n" );
    }
}
Some things to note are that:

Opening a Simple Features File

In order to open a file with OGIS simple features information in it, the following steps should be followed. First, it is necessary to identify the provider to use. In this example the user gives the provider name as a string, perhaps after having reviewed a list of possible providers from SFCDumpProviders(). Second, the filename is needed for the creation of an SFCDataStore.

static SFCDataSource * SFCOpenDataSource( const char * pszProvider, 
                                          const char * pszDataSource )

{
    SFCEnumerator      oEnumerator;

    if( FAILED(oEnumerator.Open()) )
    {
        printf( "Can't open ole db enumerator.\n" );
        return NULL;
    }

    if( !oEnumerator.Find((char*) pszProvider) )
    {
        printf( "Can't find OLE DB provider `%s'.\n", pszProvider );
        return NULL;
    }

    SFCDataSource *poDS;

    poDS = new SFCDataSource;

    if( FAILED(poDS->Open( oEnumerator, pszDataSource  )) )
    {
        delete poDS;
        printf( "Attempt to access datasource %s failed.\n", 
                pszDataSource );
        return NULL;
    }
    else
        return poDS;
}
Some interesting things to note are that:

Accessing an SFCTable

The SFCDataSource that we opened could have a number of spatial, and non spatial tables in it. Eventually the SFCDataSource will include methods for identifying these. For the time being it is necessary to know apriori what the table (layer) to access is called. The following code shows how to instantiate an SFCTable from within a data store.

    SFCDataSource      *poDS;

    poDS = SFCOpenDataSource( pszProvider, pszDataSource );
    if( poDS == NULL )
        goto CleanupAndExit;

    SFCTable      *poTable;

    poTable = poDS->CreateSFCTable( pszTable );

    delete poDS;

    if( poTable == NULL )
    {
        printf( "Failed to open table %s.\n",  pszTable );
        goto CleanupAndExit;
    }
Things to note are:

Reading the Features

The following example shows reading through all the records in a simple features table, and reading back just the geometry column. It is instantiated into a geometry object, which is then dumped to stdout.

static void SFCDumpTableGeometry( SFCTable * poTable )

{
    HRESULT      hr;

    while( !FAILED((hr = poTable->MoveNext())) )
    {
        OGRGeometry * poGeom;

        poGeom = poTable->GetOGRGeometry();
        poTable->ReleaseIUnknowns();

        if( poGeom == NULL )
        {
            printf( "Failed to reconstitute geometry!\n" );
            break;
        }
        else
        {
            poGeom->dumpReadable( stdout ); 
            delete poGeom;
        }
    }
}
Things to note:
Items to add:
  1. Information on getting the schema.

  2. Information on accessing other attribute fields.

  3. Rewrite error handling when error handling semantics worked out.

  4. Rewrite table selection when table selection semantics are available.

*/