| 1 |
/*! \page sfc_tutorial Simple Features Client Tutorial |
|---|
| 2 |
|
|---|
| 3 |
\section sfc_tutorial_intro Introduction |
|---|
| 4 |
|
|---|
| 5 |
The Simple Features Client API is a set of classes, and support functions |
|---|
| 6 |
that simplify access to OpenGIS SF OLE DB providers. <p> |
|---|
| 7 |
|
|---|
| 8 |
Most of the classes are based on the ATL OLE DB consumer templates. To fully |
|---|
| 9 |
understand the classes it is critical to also review the Microsoft provided |
|---|
| 10 |
information on these classes. It can be accessed in the <a |
|---|
| 11 |
href="http://msdn.microsoft.com/">MSDN Online Library</a>. <p> |
|---|
| 12 |
|
|---|
| 13 |
In general terms, the OpenGIS SF COM interface specification requires |
|---|
| 14 |
geographic data providers to make their datasets look like a database. |
|---|
| 15 |
A <b>data source</b> (usually a single data file, or group of related data |
|---|
| 16 |
files) can contain one or more spatial tables which roughly correspond to |
|---|
| 17 |
what be called layers in most systems. Each table has a schema defining the |
|---|
| 18 |
set of attributes (table fields) that apply to each feature in that table |
|---|
| 19 |
(layer). Each record in the table corresponds to a features, with a |
|---|
| 20 |
set of attributes, and some geometry. The geometry is stored in one field |
|---|
| 21 |
of the table as a BLOB (binary large object) encoded in a particular format |
|---|
| 22 |
(OpenGIS SF Well Known Binary Format). <p> |
|---|
| 23 |
|
|---|
| 24 |
The Simple Features Client classes help model the different parts of this |
|---|
| 25 |
data model:<p> |
|---|
| 26 |
|
|---|
| 27 |
<ul> |
|---|
| 28 |
|
|---|
| 29 |
<li> SFCEnumerator: This class allows the application to look through all |
|---|
| 30 |
the OLE DB drivers installed on this system, including the ability to identify |
|---|
| 31 |
which of these claim to be OpenGIS data providers.<p> |
|---|
| 32 |
|
|---|
| 33 |
<li> SFCDataSource: This class allows a file to be opened with a data provider |
|---|
| 34 |
(driver) selected from the SFCEnumerator. It also provides a method to |
|---|
| 35 |
instantiate an SFCTable for a particular table (layer) in that data source. |
|---|
| 36 |
Eventually it will include methods to identify all the tables in the data |
|---|
| 37 |
source, and to identify which of these is spatially enabled. Some other |
|---|
| 38 |
metadata about the datastore will also be available from this class.<p> |
|---|
| 39 |
|
|---|
| 40 |
<li> SFCTable: This class allows a table (layer) in a data source to be read. |
|---|
| 41 |
It includes SF COM related methods for extracting the geometry column and |
|---|
| 42 |
translating them into in memory geometry objects using the |
|---|
| 43 |
OGRGeometryFactory. |
|---|
| 44 |
|
|---|
| 45 |
<li> OGRGeometry: This is the base class for all types of simple features |
|---|
| 46 |
geometry. Geometry objects returned from an SFCTable will be of a class |
|---|
| 47 |
derived from OGRGeometry, and be queried with a variety of methods. |
|---|
| 48 |
Review the <a href="hierarchy.html">class hierarchy</a> to learn more about |
|---|
| 49 |
the possible geometry types. |
|---|
| 50 |
|
|---|
| 51 |
</ul> |
|---|
| 52 |
|
|---|
| 53 |
\section sfc_tutorial_example Example Client |
|---|
| 54 |
|
|---|
| 55 |
The console program SFCDUMP is intended to be an example of how to use |
|---|
| 56 |
the client api (as well as being a useful debugging tool in it's own |
|---|
| 57 |
right). The full source should be available in |
|---|
| 58 |
<a href="http://svn.osgeo.org/gdal/trunk/gdal/ogr/sfcdump.cpp" title="sfcdump.cpp linked directly from GDAL SVN">gdal/ogr/sfcdump.cpp</a>.<p> |
|---|
| 59 |
|
|---|
| 60 |
|
|---|
| 61 |
\subsection sfc_tutorial_enum SFCEnumerator: Finding a Provider |
|---|
| 62 |
|
|---|
| 63 |
The SFCEnumerator class is used to identify all the installed OLE DB |
|---|
| 64 |
providers (drivers) installed on a system. It essentially makes the |
|---|
| 65 |
list of OLE DB providers appear to be a table. The following code |
|---|
| 66 |
sequence demonstrates how to create an SFCEnumerator, loop through all |
|---|
| 67 |
the records, and print out some information for each record.<p> |
|---|
| 68 |
|
|---|
| 69 |
<pre> |
|---|
| 70 |
static void SFCDumpProviders() |
|---|
| 71 |
|
|---|
| 72 |
{ |
|---|
| 73 |
SFCEnumerator oEnum; |
|---|
| 74 |
|
|---|
| 75 |
printf( "Available OLE DB Providers\n" ); |
|---|
| 76 |
printf( "==========================\n" ); |
|---|
| 77 |
|
|---|
| 78 |
if( FAILED(oEnum.Open()) ) |
|---|
| 79 |
{ |
|---|
| 80 |
printf( "Failed to initialize SFCEnumerator.\n" ); |
|---|
| 81 |
return; |
|---|
| 82 |
} |
|---|
| 83 |
|
|---|
| 84 |
while( oEnum.MoveNext() == S_OK ) |
|---|
| 85 |
{ |
|---|
| 86 |
printf( "%S: %S\n", |
|---|
| 87 |
oEnum.m_szName, oEnum.m_szDescription ); |
|---|
| 88 |
|
|---|
| 89 |
if( oEnum.IsOGISProvider() ) |
|---|
| 90 |
printf( " (OGISDataProvider)\n" ); |
|---|
| 91 |
|
|---|
| 92 |
printf( "\n" ); |
|---|
| 93 |
} |
|---|
| 94 |
} |
|---|
| 95 |
</pre> |
|---|
| 96 |
|
|---|
| 97 |
Some things to note are that: |
|---|
| 98 |
|
|---|
| 99 |
<ul> |
|---|
| 100 |
<li> The m_szName, and m_szDescription data members are unicode strings, |
|---|
| 101 |
so we have to use %S instead of %s to display them. <p> |
|---|
| 102 |
|
|---|
| 103 |
<li> The MoveNext() method is used to go from record to record. This is |
|---|
| 104 |
the same method used to read records in an SFCTable. <p> |
|---|
| 105 |
|
|---|
| 106 |
<li> The IsOGISProvider() method will query the registry to see if the |
|---|
| 107 |
provider claims to be an OGISDataProvider. Generic databases such as |
|---|
| 108 |
MS Access can still be used to store spatial data, but they will generally |
|---|
| 109 |
be less completely configured, and less capable than a OGIS data provider.<p> |
|---|
| 110 |
|
|---|
| 111 |
<li> The SFCEnumerator hides all the COM machinery such as |
|---|
| 112 |
instantiating COM objects, querying for interfaces and maintaining reference |
|---|
| 113 |
counts.<p> |
|---|
| 114 |
|
|---|
| 115 |
</ul> |
|---|
| 116 |
|
|---|
| 117 |
\subsection sfc_tutorial_open Opening a Simple Features File |
|---|
| 118 |
|
|---|
| 119 |
In order to open a file with OGIS simple features information in it, the |
|---|
| 120 |
following steps should be followed. First, it is necessary to identify the |
|---|
| 121 |
provider to use. In this example the user gives the provider name as a |
|---|
| 122 |
string, perhaps after having reviewed a list of possible providers from |
|---|
| 123 |
SFCDumpProviders(). Second, the filename is needed for the creation of |
|---|
| 124 |
an SFCDataStore.<p> |
|---|
| 125 |
|
|---|
| 126 |
<pre> |
|---|
| 127 |
static SFCDataSource * SFCOpenDataSource( const char * pszProvider, |
|---|
| 128 |
const char * pszDataSource ) |
|---|
| 129 |
|
|---|
| 130 |
{ |
|---|
| 131 |
SFCEnumerator oEnumerator; |
|---|
| 132 |
|
|---|
| 133 |
if( FAILED(oEnumerator.Open()) ) |
|---|
| 134 |
{ |
|---|
| 135 |
printf( "Can't open ole db enumerator.\n" ); |
|---|
| 136 |
return NULL; |
|---|
| 137 |
} |
|---|
| 138 |
|
|---|
| 139 |
if( !oEnumerator.Find((char*) pszProvider) ) |
|---|
| 140 |
{ |
|---|
| 141 |
printf( "Can't find OLE DB provider `%s'.\n", pszProvider ); |
|---|
| 142 |
return NULL; |
|---|
| 143 |
} |
|---|
| 144 |
|
|---|
| 145 |
SFCDataSource *poDS; |
|---|
| 146 |
|
|---|
| 147 |
poDS = new SFCDataSource; |
|---|
| 148 |
|
|---|
| 149 |
if( FAILED(poDS->Open( oEnumerator, pszDataSource )) ) |
|---|
| 150 |
{ |
|---|
| 151 |
delete poDS; |
|---|
| 152 |
printf( "Attempt to access datasource %s failed.\n", |
|---|
| 153 |
pszDataSource ); |
|---|
| 154 |
return NULL; |
|---|
| 155 |
} |
|---|
| 156 |
else |
|---|
| 157 |
return poDS; |
|---|
| 158 |
} |
|---|
| 159 |
</pre> |
|---|
| 160 |
|
|---|
| 161 |
Some interesting things to note are that:<p> |
|---|
| 162 |
|
|---|
| 163 |
<ul> |
|---|
| 164 |
<li> We open the SFCEnumerator the same as in the previous example, but |
|---|
| 165 |
use the Find() method to find the record with a particular m_pszName. We |
|---|
| 166 |
could have read through record by record and stopped on the record we wanted |
|---|
| 167 |
instead.<p> |
|---|
| 168 |
|
|---|
| 169 |
<li> The SFCDataSource::Open() method takes in an enumerator. The data |
|---|
| 170 |
source is created based on the provider of the currently selected row in |
|---|
| 171 |
the passed enumerator. The other argument is the filename to open.<p> |
|---|
| 172 |
|
|---|
| 173 |
<li> The SFCEnumerator isn't needed after the SFCDataSource has been |
|---|
| 174 |
created, so we let it fall out of scope and be destroyed at the end of |
|---|
| 175 |
the function.<p> |
|---|
| 176 |
|
|---|
| 177 |
<li> The provider and data source strings are all passed as simple C |
|---|
| 178 |
strings. The classes take care of internally translating to unicode when |
|---|
| 179 |
needed.<p> |
|---|
| 180 |
|
|---|
| 181 |
</ul> |
|---|
| 182 |
|
|---|
| 183 |
\subsection sfc_tutorial_access Accessing an SFCTable |
|---|
| 184 |
|
|---|
| 185 |
The SFCDataSource that we opened could have a number of spatial, and non |
|---|
| 186 |
spatial tables in it. Eventually the SFCDataSource will include methods for |
|---|
| 187 |
identifying these. For the time being it is necessary to know apriori what |
|---|
| 188 |
the table (layer) to access is called. The following code shows how to |
|---|
| 189 |
instantiate an SFCTable from within a data store.<p> |
|---|
| 190 |
|
|---|
| 191 |
<pre> |
|---|
| 192 |
SFCDataSource *poDS; |
|---|
| 193 |
|
|---|
| 194 |
poDS = SFCOpenDataSource( pszProvider, pszDataSource ); |
|---|
| 195 |
if( poDS == NULL ) |
|---|
| 196 |
goto CleanupAndExit; |
|---|
| 197 |
|
|---|
| 198 |
SFCTable *poTable; |
|---|
| 199 |
|
|---|
| 200 |
poTable = poDS->CreateSFCTable( pszTable ); |
|---|
| 201 |
|
|---|
| 202 |
delete poDS; |
|---|
| 203 |
|
|---|
| 204 |
if( poTable == NULL ) |
|---|
| 205 |
{ |
|---|
| 206 |
printf( "Failed to open table %s.\n", pszTable ); |
|---|
| 207 |
goto CleanupAndExit; |
|---|
| 208 |
} |
|---|
| 209 |
</pre> |
|---|
| 210 |
|
|---|
| 211 |
Things to note are: |
|---|
| 212 |
|
|---|
| 213 |
<ul> |
|---|
| 214 |
<li> After creating the SFCTable we no longer need the SFCDataSource, so |
|---|
| 215 |
we delete it. The underlying data source/file remains open because the |
|---|
| 216 |
SFCTable is using it, but the SFCDataSource object no longer exists holding |
|---|
| 217 |
a reference on the data source.<p> |
|---|
| 218 |
|
|---|
| 219 |
<li> There is little information provided about what error occured if |
|---|
| 220 |
things go wrong within the CreateSFCTable() method or others. Eventually the |
|---|
| 221 |
SFC classes may through exceptions with more information when things go |
|---|
| 222 |
wrong. <p> |
|---|
| 223 |
|
|---|
| 224 |
</ul> |
|---|
| 225 |
|
|---|
| 226 |
\subsection sfc_tutorial_read Reading the Features |
|---|
| 227 |
|
|---|
| 228 |
The following example shows reading through all the records in a |
|---|
| 229 |
simple features table, and reading back just the geometry column. |
|---|
| 230 |
It is instantiated into a geometry object, which is then dumped |
|---|
| 231 |
to stdout. <p> |
|---|
| 232 |
|
|---|
| 233 |
<pre> |
|---|
| 234 |
static void SFCDumpTableGeometry( SFCTable * poTable ) |
|---|
| 235 |
|
|---|
| 236 |
{ |
|---|
| 237 |
HRESULT hr; |
|---|
| 238 |
|
|---|
| 239 |
while( !FAILED((hr = poTable->MoveNext())) ) |
|---|
| 240 |
{ |
|---|
| 241 |
OGRGeometry * poGeom; |
|---|
| 242 |
|
|---|
| 243 |
poGeom = poTable->GetOGRGeometry(); |
|---|
| 244 |
poTable->ReleaseIUnknowns(); |
|---|
| 245 |
|
|---|
| 246 |
if( poGeom == NULL ) |
|---|
| 247 |
{ |
|---|
| 248 |
printf( "Failed to reconstitute geometry!\n" ); |
|---|
| 249 |
break; |
|---|
| 250 |
} |
|---|
| 251 |
else |
|---|
| 252 |
{ |
|---|
| 253 |
poGeom->dumpReadable( stdout ); |
|---|
| 254 |
delete poGeom; |
|---|
| 255 |
} |
|---|
| 256 |
} |
|---|
| 257 |
} |
|---|
| 258 |
</pre> |
|---|
| 259 |
|
|---|
| 260 |
Things to note: |
|---|
| 261 |
|
|---|
| 262 |
<ul> |
|---|
| 263 |
<li> The MoveNext() method is used to read records. |
|---|
| 264 |
<li> The SFCTable keeps track of which column contains the geometry. |
|---|
| 265 |
<li> It is necessary to call SFCTable::ReleaseIUnknowns() for every |
|---|
| 266 |
record read in an SFCTable! Failure to do so may cause serious |
|---|
| 267 |
problems. Calling it twice for the same record is likely to |
|---|
| 268 |
result in a crash. |
|---|
| 269 |
<li> The OGRGeometry object created by GetOGRGeometry() is now the |
|---|
| 270 |
responsibility of the caller which must delete it.<p> |
|---|
| 271 |
<li> While note shown here, there is also a GetWKBGeometry() method on |
|---|
| 272 |
the SFCTable for applications that want the binary form of the |
|---|
| 273 |
geometry for their own purposes. <p> |
|---|
| 274 |
<li> All the other fields in this table are also available; however, this |
|---|
| 275 |
example doesn't show how to access them.<p> |
|---|
| 276 |
</ul> |
|---|
| 277 |
|
|---|
| 278 |
<hr> |
|---|
| 279 |
|
|---|
| 280 |
Items to add: |
|---|
| 281 |
|
|---|
| 282 |
<ol> |
|---|
| 283 |
<li> Information on getting the schema.<p> |
|---|
| 284 |
<li> Information on accessing other attribute fields.<p> |
|---|
| 285 |
<li> Rewrite error handling when error handling semantics worked out.<p> |
|---|
| 286 |
<li> Rewrite table selection when table selection semantics are available.<p> |
|---|
| 287 |
</ol> |
|---|
| 288 |
|
|---|
| 289 |
\htmlonly |
|---|
| 290 |
<p> |
|---|
| 291 |
$Id$ |
|---|
| 292 |
</p> |
|---|
| 293 |
\endhtmlonly |
|---|
| 294 |
|
|---|
| 295 |
*/ |
|---|