1 : /******************************************************************************
2 : * $Id: FGdbDatasource.cpp 25025 2012-10-01 21:06:59Z rouault $
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Implements FileGDB OGR Datasource.
6 : * Author: Ragi Yaser Burhum, ragi@burhum.com
7 : * Paul Ramsey, pramsey at cleverelephant.ca
8 : *
9 : ******************************************************************************
10 : * Copyright (c) 2010, Ragi Yaser Burhum
11 : * Copyright (c) 2011, Paul Ramsey <pramsey at cleverelephant.ca>
12 : *
13 : * Permission is hereby granted, free of charge, to any person obtaining a
14 : * copy of this software and associated documentation files (the "Software"),
15 : * to deal in the Software without restriction, including without limitation
16 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 : * and/or sell copies of the Software, and to permit persons to whom the
18 : * Software is furnished to do so, subject to the following conditions:
19 : *
20 : * The above copyright notice and this permission notice shall be included
21 : * in all copies or substantial portions of the Software.
22 : *
23 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 : * DEALINGS IN THE SOFTWARE.
30 : ****************************************************************************/
31 :
32 : #include "ogr_fgdb.h"
33 : #include "cpl_conv.h"
34 : #include "cpl_string.h"
35 : #include "gdal.h"
36 : #include "FGdbUtils.h"
37 :
38 : CPL_CVSID("$Id: FGdbDatasource.cpp 25025 2012-10-01 21:06:59Z rouault $");
39 :
40 : using std::vector;
41 : using std::wstring;
42 :
43 : /************************************************************************/
44 : /* FGdbDataSource() */
45 : /************************************************************************/
46 :
47 23 : FGdbDataSource::FGdbDataSource():
48 : OGRDataSource(),
49 23 : m_pszName(0), m_pGeodatabase(NULL)
50 : {
51 23 : }
52 :
53 : /************************************************************************/
54 : /* ~FGdbDataSource() */
55 : /************************************************************************/
56 :
57 23 : FGdbDataSource::~FGdbDataSource()
58 : {
59 23 : CPLFree( m_pszName );
60 :
61 23 : size_t count = m_layers.size();
62 203 : for(size_t i = 0; i < count; ++i )
63 180 : delete m_layers[i];
64 :
65 23 : if (m_pGeodatabase)
66 : {
67 23 : ::CloseGeodatabase(*m_pGeodatabase);
68 23 : delete m_pGeodatabase;
69 : }
70 23 : }
71 :
72 :
73 : /************************************************************************/
74 : /* Open() */
75 : /************************************************************************/
76 :
77 23 : int FGdbDataSource::Open(Geodatabase* pGeodatabase, const char * pszNewName, int bUpdate )
78 : {
79 23 : m_pszName = CPLStrdup( pszNewName );
80 23 : m_pGeodatabase = pGeodatabase;
81 :
82 23 : std::vector<std::wstring> typesRequested;
83 :
84 : // We're only interested in Tables, Feature Datasets and Feature Classes
85 23 : typesRequested.push_back(L"Feature Class");
86 46 : typesRequested.push_back(L"Table");
87 46 : typesRequested.push_back(L"Feature Dataset");
88 :
89 46 : bool rv = LoadLayers(L"\\");
90 :
91 23 : return rv;
92 : }
93 :
94 : /************************************************************************/
95 : /* OpenFGDBTables() */
96 : /************************************************************************/
97 :
98 24 : bool FGdbDataSource::OpenFGDBTables(const std::wstring &type,
99 : const std::vector<std::wstring> &layers)
100 : {
101 : fgdbError hr;
102 171 : for ( unsigned int i = 0; i < layers.size(); i++ )
103 : {
104 147 : Table* pTable = new Table;
105 : //CPLDebug("FGDB", "Opening %s", WStringToString(layers[i]).c_str());
106 147 : if (FAILED(hr = m_pGeodatabase->OpenTable(layers[i], *pTable)))
107 : {
108 0 : delete pTable;
109 0 : GDBDebug(hr, "Error opening " + WStringToString(layers[i]) + ". Skipping it");
110 0 : continue;
111 : }
112 147 : FGdbLayer* pLayer = new FGdbLayer;
113 294 : if (!pLayer->Initialize(this, pTable, layers[i], type))
114 : {
115 0 : delete pLayer;
116 0 : return GDBErr(hr, "Error initializing OGRLayer for " + WStringToString(layers[i]));
117 : }
118 :
119 147 : m_layers.push_back(pLayer);
120 : }
121 24 : return true;
122 : }
123 :
124 : /************************************************************************/
125 : /* LoadLayers() */
126 : /************************************************************************/
127 :
128 23 : bool FGdbDataSource::LoadLayers(const std::wstring &root)
129 : {
130 23 : std::vector<wstring> tables;
131 23 : std::vector<wstring> featureclasses;
132 23 : std::vector<wstring> featuredatasets;
133 : fgdbError hr;
134 :
135 : /* Find all the Tables in the root */
136 23 : if ( FAILED(hr = m_pGeodatabase->GetChildDatasets(root, L"Table", tables)) )
137 : {
138 0 : return GDBErr(hr, "Error reading Tables in " + WStringToString(root));
139 : }
140 : /* Open the tables we found */
141 23 : if ( tables.size() > 0 && ! OpenFGDBTables(L"Table", tables) )
142 0 : return false;
143 :
144 : /* Find all the Feature Classes in the root */
145 23 : if ( FAILED(hr = m_pGeodatabase->GetChildDatasets(root, L"Feature Class", featureclasses)) )
146 : {
147 0 : return GDBErr(hr, "Error reading Feature Classes in " + WStringToString(root));
148 : }
149 : /* Open the tables we found */
150 23 : if ( featureclasses.size() > 0 && ! OpenFGDBTables(L"Feature Class", featureclasses) )
151 0 : return false;
152 :
153 : /* Find all the Feature Datasets in the root */
154 23 : if ( FAILED(hr = m_pGeodatabase->GetChildDatasets(root, L"Feature Dataset", featuredatasets)) )
155 : {
156 0 : return GDBErr(hr, "Error reading Feature Datasets in " + WStringToString(root));
157 : }
158 : /* Look for Feature Classes inside the Feature Dataset */
159 24 : for ( unsigned int i = 0; i < featuredatasets.size(); i++ )
160 : {
161 1 : if ( FAILED(hr = m_pGeodatabase->GetChildDatasets(featuredatasets[i], L"Feature Class", featureclasses)) )
162 : {
163 0 : return GDBErr(hr, "Error reading Feature Classes in " + WStringToString(featuredatasets[i]));
164 : }
165 1 : if ( featureclasses.size() > 0 && ! OpenFGDBTables(L"Feature Class", featureclasses) )
166 0 : return false;
167 : }
168 23 : return true;
169 : }
170 :
171 :
172 : #if 0
173 : /************************************************************************/
174 : /* LoadLayersOld() */
175 : /************************************************************************/
176 :
177 : /* Old recursive LoadLayers. Removed in favor of simple one that only
178 : looks at FeatureClasses and Tables. */
179 :
180 : // Flattens out hierarchichal GDB structure
181 : bool FGdbDataSource::LoadLayersOld(const std::vector<wstring> & datasetTypes,
182 : const wstring & parent)
183 : {
184 : long hr = S_OK;
185 :
186 : // I didn't find an API to give me the type of the dataset based on name - I am *not*
187 : // parsing XML for something like this - in the meantime I can use this hack to see
188 : // if the dataset had any children whatsoever - if so, then I won't attempt to open it
189 : // otherwise, do attempt to do that
190 :
191 : bool childrenFound = false;
192 : bool errorsEncountered = false;
193 :
194 : for (size_t dsTypeIndex = 0; dsTypeIndex < datasetTypes.size(); dsTypeIndex++)
195 : {
196 : std::vector<wstring> childDatasets;
197 : m_pGeodatabase->GetChildDatasets( parent, datasetTypes[dsTypeIndex], childDatasets);
198 :
199 : if (childDatasets.size() > 0)
200 : {
201 : //it is a container of other datasets
202 :
203 : for (size_t childDatasetIndex = 0;
204 : childDatasetIndex < childDatasets.size();
205 : childDatasetIndex++)
206 : {
207 : childrenFound = true;
208 :
209 : // do something with it
210 : // For now, we just ignore dataset containers and only open the children
211 : //std::wcout << datasetTypes[dsTypeIndex] << L" " << childDatasets[childDatasetIndex] << std::endl;
212 :
213 : if (!LoadLayersOld(datasetTypes, childDatasets[childDatasetIndex]))
214 : errorsEncountered = true;
215 : }
216 : }
217 : }
218 :
219 : //it is a full fledged dataset itself without children - open it (except the root)
220 :
221 : if ((!childrenFound) && parent != L"\\")
222 : {
223 : //wcout << "Opening " << parent << "...";
224 : Table* pTable = new Table;
225 : if (FAILED(hr = m_pGeodatabase->OpenTable(parent,*pTable)))
226 : {
227 : delete pTable;
228 : return GDBErr(hr, "Error opening " + WStringToString(parent));
229 : }
230 :
231 : FGdbLayer* pLayer = new FGdbLayer;
232 :
233 : //pLayer has ownership of the table pointer as soon Initialize is called
234 : if (!pLayer->Initialize(this, pTable, parent))
235 : {
236 : delete pLayer;
237 :
238 : return GDBErr(hr, "Error initializing OGRLayer for " +
239 : WStringToString(parent));
240 : }
241 :
242 : m_layers.push_back(pLayer);
243 : }
244 :
245 : return !errorsEncountered;
246 : }
247 : #endif
248 :
249 :
250 : /************************************************************************/
251 : /* DeleteLayer() */
252 : /************************************************************************/
253 :
254 2 : OGRErr FGdbDataSource::DeleteLayer( int iLayer )
255 : {
256 2 : if( iLayer < 0 || iLayer >= static_cast<int>(m_layers.size()) )
257 0 : return OGRERR_FAILURE;
258 :
259 : // Fetch FGDBAPI Table before deleting OGR layer object
260 :
261 2 : Table* pTable = m_layers[iLayer]->GetTable();
262 :
263 2 : std::string name = m_layers[iLayer]->GetLayerDefn()->GetName();
264 4 : std::wstring strPath = m_layers[iLayer]->GetTablePath();
265 2 : std::wstring strType = m_layers[iLayer]->GetType();
266 :
267 : // delete OGR layer
268 2 : delete m_layers[iLayer];
269 :
270 2 : pTable = NULL; // OGR Layer had ownership of FGDB Table
271 :
272 2 : m_layers.erase(m_layers.begin() + iLayer);
273 :
274 : long hr;
275 :
276 2 : if (FAILED(hr = m_pGeodatabase->Delete(strPath, strType)))
277 : {
278 : CPLError( CE_Warning, CPLE_AppDefined,
279 0 : "%s was not deleted however it has been closed", name.c_str());
280 0 : GDBErr(hr, "Failed deleting dataset");
281 0 : return OGRERR_FAILURE;
282 : }
283 :
284 2 : return OGRERR_NONE;
285 : }
286 :
287 : /************************************************************************/
288 : /* TestCapability() */
289 : /************************************************************************/
290 :
291 3 : int FGdbDataSource::TestCapability( const char * pszCap )
292 : {
293 3 : if( EQUAL(pszCap,ODsCCreateLayer) )
294 1 : return TRUE;
295 :
296 2 : else if( EQUAL(pszCap,ODsCDeleteLayer) )
297 0 : return TRUE;
298 :
299 2 : return FALSE;
300 : }
301 :
302 :
303 : /************************************************************************/
304 : /* GetLayer() */
305 : /************************************************************************/
306 :
307 2005 : OGRLayer *FGdbDataSource::GetLayer( int iLayer )
308 : {
309 2005 : int count = static_cast<int>(m_layers.size());
310 :
311 2005 : if( iLayer < 0 || iLayer >= count )
312 4 : return NULL;
313 : else
314 2001 : return m_layers[iLayer];
315 : }
316 :
317 : /************************************************************************/
318 : /* CreateLayer() */
319 : /* */
320 : /* See FGdbLayer::Create for creation options */
321 : /************************************************************************/
322 :
323 : OGRLayer *
324 38 : FGdbDataSource::CreateLayer( const char * pszLayerName,
325 : OGRSpatialReference *poSRS,
326 : OGRwkbGeometryType eType,
327 : char ** papszOptions )
328 : {
329 38 : FGdbLayer* pLayer = new FGdbLayer;
330 38 : if (!pLayer->Create(this, pszLayerName, poSRS, eType, papszOptions))
331 : {
332 3 : delete pLayer;
333 3 : return NULL;
334 : }
335 :
336 35 : m_layers.push_back(pLayer);
337 :
338 35 : return pLayer;
339 : }
340 :
341 :
342 : /************************************************************************/
343 : /* OGRFGdbSingleFeatureLayer */
344 : /************************************************************************/
345 :
346 : class OGRFGdbSingleFeatureLayer : public OGRLayer
347 : {
348 : private:
349 : char *pszVal;
350 : OGRFeatureDefn *poFeatureDefn;
351 : int iNextShapeId;
352 :
353 : public:
354 : OGRFGdbSingleFeatureLayer( const char* pszLayerName,
355 : const char *pszVal );
356 : ~OGRFGdbSingleFeatureLayer();
357 :
358 0 : virtual void ResetReading() { iNextShapeId = 0; }
359 : virtual OGRFeature *GetNextFeature();
360 0 : virtual OGRFeatureDefn *GetLayerDefn() { return poFeatureDefn; }
361 0 : virtual int TestCapability( const char * ) { return FALSE; }
362 : };
363 :
364 : /************************************************************************/
365 : /* OGRFGdbSingleFeatureLayer() */
366 : /************************************************************************/
367 :
368 30 : OGRFGdbSingleFeatureLayer::OGRFGdbSingleFeatureLayer(const char* pszLayerName,
369 30 : const char *pszVal )
370 : {
371 30 : poFeatureDefn = new OGRFeatureDefn( pszLayerName );
372 30 : poFeatureDefn->Reference();
373 30 : OGRFieldDefn oField( "FIELD_1", OFTString );
374 30 : poFeatureDefn->AddFieldDefn( &oField );
375 :
376 30 : iNextShapeId = 0;
377 30 : this->pszVal = pszVal ? CPLStrdup(pszVal) : NULL;
378 30 : }
379 :
380 : /************************************************************************/
381 : /* ~OGRFGdbSingleFeatureLayer() */
382 : /************************************************************************/
383 :
384 30 : OGRFGdbSingleFeatureLayer::~OGRFGdbSingleFeatureLayer()
385 : {
386 30 : if( poFeatureDefn != NULL )
387 30 : poFeatureDefn->Release();
388 30 : CPLFree(pszVal);
389 30 : }
390 :
391 :
392 : /************************************************************************/
393 : /* GetNextFeature() */
394 : /************************************************************************/
395 :
396 0 : OGRFeature * OGRFGdbSingleFeatureLayer::GetNextFeature()
397 : {
398 0 : if (iNextShapeId != 0)
399 0 : return NULL;
400 :
401 0 : OGRFeature* poFeature = new OGRFeature(poFeatureDefn);
402 0 : if (pszVal)
403 0 : poFeature->SetField(0, pszVal);
404 0 : poFeature->SetFID(iNextShapeId ++);
405 0 : return poFeature;
406 : }
407 :
408 : /************************************************************************/
409 : /* ExecuteSQL() */
410 : /************************************************************************/
411 :
412 66 : OGRLayer * FGdbDataSource::ExecuteSQL( const char *pszSQLCommand,
413 : OGRGeometry *poSpatialFilter,
414 : const char *pszDialect )
415 :
416 : {
417 66 : if ( pszDialect != NULL && EQUAL(pszDialect, "OGRSQL") )
418 : return OGRDataSource::ExecuteSQL( pszSQLCommand,
419 : poSpatialFilter,
420 0 : pszDialect );
421 :
422 : /* -------------------------------------------------------------------- */
423 : /* Special case GetLayerDefinition */
424 : /* -------------------------------------------------------------------- */
425 66 : if (EQUALN(pszSQLCommand, "GetLayerDefinition ", strlen("GetLayerDefinition ")))
426 : {
427 15 : FGdbLayer* poLayer = (FGdbLayer*) GetLayerByName(pszSQLCommand + strlen("GetLayerDefinition "));
428 15 : if (poLayer)
429 : {
430 15 : char* pszVal = NULL;
431 15 : poLayer->GetLayerXML(&pszVal);
432 15 : OGRLayer* poRet = new OGRFGdbSingleFeatureLayer( "LayerDefinition", pszVal );
433 15 : CPLFree(pszVal);
434 15 : return poRet;
435 : }
436 : else
437 0 : return NULL;
438 : }
439 :
440 : /* -------------------------------------------------------------------- */
441 : /* Special case GetLayerMetadata */
442 : /* -------------------------------------------------------------------- */
443 51 : if (EQUALN(pszSQLCommand, "GetLayerMetadata ", strlen("GetLayerMetadata ")))
444 : {
445 15 : FGdbLayer* poLayer = (FGdbLayer*) GetLayerByName(pszSQLCommand + strlen("GetLayerMetadata "));
446 15 : if (poLayer)
447 : {
448 15 : char* pszVal = NULL;
449 15 : poLayer->GetLayerMetadataXML(&pszVal);
450 15 : OGRLayer* poRet = new OGRFGdbSingleFeatureLayer( "LayerMetadata", pszVal );
451 15 : CPLFree(pszVal);
452 15 : return poRet;
453 : }
454 : else
455 0 : return NULL;
456 : }
457 :
458 : /* TODO: remove that workaround when the SDK has finally a decent */
459 : /* SQL support ! */
460 36 : if( EQUALN(pszSQLCommand, "SELECT ", 7) && pszDialect == NULL )
461 : {
462 : CPLDebug("FGDB", "Support for SELECT is known to be partially "
463 : "non-compliant with FileGDB SDK API v1.2.\n"
464 : "So for now, we use default OGR SQL engine. "
465 : "Explicitely specify -dialect FileGDB\n"
466 32 : "to use the SQL engine from the FileGDB SDK API");
467 : return OGRDataSource::ExecuteSQL( pszSQLCommand,
468 : poSpatialFilter,
469 32 : pszDialect );
470 : }
471 :
472 : /* -------------------------------------------------------------------- */
473 : /* Run the SQL */
474 : /* -------------------------------------------------------------------- */
475 4 : EnumRows* pEnumRows = new EnumRows;
476 : long hr;
477 8 : if (FAILED(hr = m_pGeodatabase->ExecuteSQL(
478 : StringToWString(pszSQLCommand), true, *pEnumRows)))
479 : {
480 2 : GDBErr(hr, CPLSPrintf("Failed at executing '%s'", pszSQLCommand));
481 4 : delete pEnumRows;
482 2 : return NULL;
483 : }
484 :
485 2 : if( EQUALN(pszSQLCommand, "SELECT ", 7) )
486 : {
487 1 : return new FGdbResultLayer(this, pszSQLCommand, pEnumRows);
488 : }
489 : else
490 : {
491 1 : delete pEnumRows;
492 1 : return NULL;
493 : }
494 : }
495 :
496 : /************************************************************************/
497 : /* ReleaseResultSet() */
498 : /************************************************************************/
499 :
500 63 : void FGdbDataSource::ReleaseResultSet( OGRLayer * poResultsSet )
501 : {
502 63 : delete poResultsSet;
503 2202 : }
|