1 : /******************************************************************************
2 : * $Id: FGdbLayer.cpp 25208 2012-11-05 18:34:41Z rouault $
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Implements FileGDB OGR layer.
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 "ogrpgeogeometry.h"
34 : #include "cpl_conv.h"
35 : #include "cpl_string.h"
36 : #include "FGdbUtils.h"
37 : #include "cpl_minixml.h" // the only way right now to extract schema information
38 :
39 : CPL_CVSID("$Id: FGdbLayer.cpp 25208 2012-11-05 18:34:41Z rouault $");
40 :
41 : using std::string;
42 : using std::wstring;
43 :
44 : /************************************************************************/
45 : /* FGdbBaseLayer() */
46 : /************************************************************************/
47 186 : FGdbBaseLayer::FGdbBaseLayer() :
48 : m_pFeatureDefn(NULL), m_pSRS(NULL), m_pEnumRows(NULL),
49 186 : m_supressColumnMappingError(false), m_forceMulti(false)
50 : {
51 186 : }
52 :
53 : /************************************************************************/
54 : /* ~FGdbBaseLayer() */
55 : /************************************************************************/
56 186 : FGdbBaseLayer::~FGdbBaseLayer()
57 : {
58 186 : if (m_pFeatureDefn)
59 : {
60 183 : m_pFeatureDefn->Release();
61 183 : m_pFeatureDefn = NULL;
62 : }
63 :
64 186 : if (m_pEnumRows)
65 : {
66 186 : delete m_pEnumRows;
67 186 : m_pEnumRows = NULL;
68 : }
69 :
70 186 : if (m_pSRS)
71 : {
72 166 : m_pSRS->Release();
73 166 : m_pSRS = NULL;
74 : }
75 186 : }
76 :
77 : /************************************************************************/
78 : /* GetNextFeature() */
79 : /************************************************************************/
80 :
81 1062 : OGRFeature* FGdbBaseLayer::GetNextFeature()
82 : {
83 0 : while (1) //want to skip errors
84 : {
85 1062 : if (m_pEnumRows == NULL)
86 0 : return NULL;
87 :
88 : long hr;
89 :
90 1062 : Row row;
91 :
92 1062 : if (FAILED(hr = m_pEnumRows->Next(row)))
93 : {
94 0 : GDBErr(hr, "Failed fetching features");
95 0 : return NULL;
96 : }
97 :
98 1062 : if (hr != S_OK)
99 : {
100 : // It's OK, we are done fetching - failure is catched by FAILED macro
101 167 : return NULL;
102 : }
103 :
104 895 : OGRFeature* pOGRFeature = NULL;
105 :
106 895 : if (!OGRFeatureFromGdbRow(&row, &pOGRFeature))
107 : {
108 0 : int32 oid = -1;
109 0 : row.GetOID(oid);
110 :
111 0 : GDBErr(hr, CPLSPrintf("Failed translating FGDB row [%d] to OGR Feature", oid));
112 :
113 : //return NULL;
114 0 : continue; //skip feature
115 : }
116 :
117 895 : return pOGRFeature;
118 : }
119 : }
120 :
121 : /************************************************************************/
122 : /* FGdbLayer() */
123 : /************************************************************************/
124 185 : FGdbLayer::FGdbLayer():
125 : m_pDS(NULL), m_pTable(NULL), m_wstrSubfields(L"*"), m_pOGRFilterGeometry(NULL),
126 : m_bFilterDirty(true),
127 185 : m_bLaunderReservedKeywords(true)
128 : {
129 185 : m_bBulkLoadAllowed = -1; /* uninitialized */
130 185 : m_bBulkLoadInProgress = FALSE;
131 185 : m_pEnumRows = new EnumRows;
132 :
133 : #ifdef EXTENT_WORKAROUND
134 185 : m_bLayerEnvelopeValid = false;
135 185 : m_bLayerJustCreated = false;
136 : #endif
137 185 : }
138 :
139 : /************************************************************************/
140 : /* ~FGdbLayer() */
141 : /************************************************************************/
142 :
143 185 : FGdbLayer::~FGdbLayer()
144 : {
145 185 : EndBulkLoad();
146 :
147 : #ifdef EXTENT_WORKAROUND
148 185 : WorkAroundExtentProblem();
149 : #endif
150 :
151 : // NOTE: never delete m_pDS - the memory doesn't belong to us
152 : // TODO: check if we need to close the table or if the destructor
153 : // takes care of closing as it should
154 185 : if (m_pTable)
155 : {
156 182 : delete m_pTable;
157 182 : m_pTable = NULL;
158 : }
159 :
160 185 : if (m_pOGRFilterGeometry)
161 : {
162 0 : OGRGeometryFactory::destroyGeometry(m_pOGRFilterGeometry);
163 0 : m_pOGRFilterGeometry = NULL;
164 : }
165 :
166 185 : }
167 :
168 : #ifdef EXTENT_WORKAROUND
169 :
170 : /************************************************************************/
171 : /* UpdateRowWithGeometry() */
172 : /************************************************************************/
173 :
174 0 : bool FGdbLayer::UpdateRowWithGeometry(Row& row, OGRGeometry* poGeom)
175 : {
176 0 : ShapeBuffer shape;
177 : long hr;
178 :
179 : /* Write geometry to a buffer */
180 0 : GByte *pabyShape = NULL;
181 0 : int nShapeSize = 0;
182 0 : if ( OGRWriteToShapeBin( poGeom, &pabyShape, &nShapeSize ) != OGRERR_NONE )
183 0 : return false;
184 :
185 : /* Copy it into a ShapeBuffer */
186 0 : if ( nShapeSize > 0 )
187 : {
188 0 : shape.Allocate(nShapeSize);
189 0 : memcpy(shape.shapeBuffer, pabyShape, nShapeSize);
190 0 : shape.inUseLength = nShapeSize;
191 : }
192 :
193 : /* Free the shape buffer */
194 0 : CPLFree(pabyShape);
195 :
196 : /* Write ShapeBuffer into the Row */
197 0 : hr = row.SetGeometry(shape);
198 0 : if (FAILED(hr))
199 : {
200 0 : return false;
201 : }
202 :
203 : /* Update row */
204 0 : hr = m_pTable->Update(row);
205 0 : if (FAILED(hr))
206 : {
207 0 : return false;
208 : }
209 :
210 0 : return true;
211 : }
212 :
213 : /************************************************************************/
214 : /* WorkAroundExtentProblem() */
215 : /* */
216 : /* Work-around problem with FileGDB API 1.1 on Linux 64bit. See #4455 */
217 : /************************************************************************/
218 :
219 185 : void FGdbLayer::WorkAroundExtentProblem()
220 : {
221 185 : if (!m_bLayerJustCreated || !m_bLayerEnvelopeValid)
222 171 : return;
223 :
224 14 : OGREnvelope sEnvelope;
225 14 : if (GetExtent(&sEnvelope, TRUE) != OGRERR_NONE)
226 0 : return;
227 :
228 : /* The characteristic of the bug is that the reported extent */
229 : /* is the real extent truncated incorrectly to integer values */
230 : /* We work around that by temporary updating one feature with a geometry */
231 : /* whose coordinates are integer values but ceil'ed and floor'ed */
232 : /* such that they include the real layer extent. */
233 14 : if (((double)(int)sEnvelope.MinX == sEnvelope.MinX &&
234 : (double)(int)sEnvelope.MinY == sEnvelope.MinY &&
235 : (double)(int)sEnvelope.MaxX == sEnvelope.MaxX &&
236 : (double)(int)sEnvelope.MaxY == sEnvelope.MaxY) &&
237 : (fabs(sEnvelope.MinX - sLayerEnvelope.MinX) > 1e-5 ||
238 : fabs(sEnvelope.MinY - sLayerEnvelope.MinY) > 1e-5 ||
239 : fabs(sEnvelope.MaxX - sLayerEnvelope.MaxX) > 1e-5 ||
240 : fabs(sEnvelope.MaxY - sLayerEnvelope.MaxY) > 1e-5))
241 : {
242 : long hr;
243 0 : Row row;
244 0 : EnumRows enumRows;
245 :
246 0 : if (FAILED(hr = m_pTable->Search(StringToWString("*"), StringToWString(""), true, enumRows)))
247 : return;
248 :
249 0 : if (FAILED(hr = enumRows.Next(row)))
250 : return;
251 :
252 0 : if (hr != S_OK)
253 : return;
254 :
255 : /* Backup original shape buffer */
256 0 : ShapeBuffer originalGdbGeometry;
257 0 : if (FAILED(hr = row.GetGeometry(originalGdbGeometry)))
258 : return;
259 :
260 0 : OGRGeometry* pOGRGeo = NULL;
261 0 : if ((!GDBGeometryToOGRGeometry(m_forceMulti, &originalGdbGeometry, m_pSRS, &pOGRGeo)) || pOGRGeo == NULL)
262 : {
263 0 : delete pOGRGeo;
264 : return;
265 : }
266 :
267 0 : OGRwkbGeometryType eType = wkbFlatten(pOGRGeo->getGeometryType());
268 :
269 0 : delete pOGRGeo;
270 0 : pOGRGeo = NULL;
271 :
272 0 : OGRPoint oP1(floor(sLayerEnvelope.MinX), floor(sLayerEnvelope.MinY));
273 0 : OGRPoint oP2(ceil(sLayerEnvelope.MaxX), ceil(sLayerEnvelope.MaxY));
274 :
275 0 : OGRLinearRing oLR;
276 0 : oLR.addPoint(&oP1);
277 0 : oLR.addPoint(&oP2);
278 0 : oLR.addPoint(&oP1);
279 :
280 0 : if ( eType == wkbPoint )
281 : {
282 0 : UpdateRowWithGeometry(row, &oP1);
283 0 : UpdateRowWithGeometry(row, &oP2);
284 : }
285 0 : else if ( eType == wkbLineString )
286 : {
287 0 : UpdateRowWithGeometry(row, &oLR);
288 : }
289 0 : else if ( eType == wkbPolygon )
290 : {
291 0 : OGRPolygon oPoly;
292 0 : oPoly.addRing(&oLR);
293 :
294 0 : UpdateRowWithGeometry(row, &oPoly);
295 : }
296 0 : else if ( eType == wkbMultiPoint )
297 : {
298 0 : OGRMultiPoint oColl;
299 0 : oColl.addGeometry(&oP1);
300 0 : oColl.addGeometry(&oP2);
301 :
302 0 : UpdateRowWithGeometry(row, &oColl);
303 : }
304 0 : else if ( eType == wkbMultiLineString )
305 : {
306 0 : OGRMultiLineString oColl;
307 0 : oColl.addGeometry(&oLR);
308 :
309 0 : UpdateRowWithGeometry(row, &oColl);
310 : }
311 0 : else if ( eType == wkbMultiPolygon )
312 : {
313 0 : OGRMultiPolygon oColl;
314 0 : OGRPolygon oPoly;
315 0 : oPoly.addRing(&oLR);
316 0 : oColl.addGeometry(&oPoly);
317 :
318 0 : UpdateRowWithGeometry(row, &oColl);
319 : }
320 : else
321 : return;
322 :
323 : /* Restore original ShapeBuffer */
324 0 : hr = row.SetGeometry(originalGdbGeometry);
325 0 : if (FAILED(hr))
326 : return;
327 :
328 : /* Update Row */
329 0 : hr = m_pTable->Update(row);
330 0 : if (FAILED(hr))
331 : return;
332 :
333 0 : CPLDebug("FGDB", "Workaround extent problem with Linux 64bit FGDB SDK 1.1");
334 : }
335 : }
336 : #endif // EXTENT_WORKAROUND
337 :
338 : /************************************************************************/
339 : /* CreateFeature() */
340 : /* Create an FGDB Row and populate it from an OGRFeature. */
341 : /* */
342 : /************************************************************************/
343 :
344 1102 : OGRErr FGdbLayer::CreateFeature( OGRFeature *poFeature )
345 : {
346 1102 : Table *fgdb_table = m_pTable;
347 1102 : Row fgdb_row;
348 : fgdbError hr;
349 :
350 1102 : if (m_bBulkLoadAllowed < 0)
351 34 : m_bBulkLoadAllowed = CSLTestBoolean(CPLGetConfigOption("FGDB_BULK_LOAD", "NO"));
352 :
353 1102 : if (m_bBulkLoadAllowed && !m_bBulkLoadInProgress)
354 1 : StartBulkLoad();
355 :
356 1102 : hr = fgdb_table->CreateRowObject(fgdb_row);
357 :
358 : /* Check the status of the Row create */
359 1102 : if (FAILED(hr))
360 : {
361 0 : GDBErr(hr, "Failed at creating Row in CreateFeature.");
362 0 : return OGRERR_FAILURE;
363 : }
364 :
365 : /* Populate the row with the feature content */
366 1102 : if (PopulateRowWithFeature(fgdb_row, poFeature) != OGRERR_NONE)
367 0 : return OGRERR_FAILURE;
368 :
369 : /* Cannot write to FID field - it is managed by GDB*/
370 : //std::wstring wfield_name = StringToWString(m_strOIDFieldName);
371 : //hr = fgdb_row.SetInteger(wfield_name, poFeature->GetFID());
372 :
373 : /* Write the row to the table */
374 1102 : hr = fgdb_table->Insert(fgdb_row);
375 1102 : if (FAILED(hr))
376 : {
377 0 : GDBErr(hr, "Failed at writing Row to Table in CreateFeature.");
378 0 : return OGRERR_FAILURE;
379 : }
380 :
381 1102 : int32 oid = -1;
382 1102 : if (!FAILED(hr = fgdb_row.GetOID(oid)))
383 : {
384 1102 : poFeature->SetFID(oid);
385 : }
386 :
387 : #ifdef EXTENT_WORKAROUND
388 : /* For WorkAroundExtentProblem() needs */
389 1102 : OGRGeometry* poGeom = poFeature->GetGeometryRef();
390 1102 : if ( m_bLayerJustCreated && poGeom != NULL && !poGeom->IsEmpty() )
391 : {
392 1070 : OGREnvelope sFeatureGeomEnvelope;
393 1070 : poGeom->getEnvelope(&sFeatureGeomEnvelope);
394 1070 : if (!m_bLayerEnvelopeValid)
395 : {
396 14 : memcpy(&sLayerEnvelope, &sFeatureGeomEnvelope, sizeof(sLayerEnvelope));
397 14 : m_bLayerEnvelopeValid = true;
398 : }
399 : else
400 : {
401 1056 : sLayerEnvelope.Merge(sFeatureGeomEnvelope);
402 : }
403 : }
404 : #endif
405 :
406 1102 : return OGRERR_NONE;
407 : }
408 :
409 : /************************************************************************/
410 : /* PopulateRowWithFeature() */
411 : /* */
412 : /************************************************************************/
413 :
414 1167 : OGRErr FGdbLayer::PopulateRowWithFeature( Row& fgdb_row, OGRFeature *poFeature )
415 : {
416 1167 : ShapeBuffer shape;
417 : fgdbError hr;
418 :
419 1167 : OGRFeatureDefn* poFeatureDefn = m_pFeatureDefn;
420 1167 : int nFieldCount = poFeatureDefn->GetFieldCount();
421 :
422 : /* Copy the OGR visible fields (everything except geometry and FID) */
423 1167 : for( int i = 0; i < nFieldCount; i++ )
424 : {
425 1658 : std::string field_name = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
426 1658 : std::wstring wfield_name = StringToWString(field_name);
427 :
428 : /* Set empty fields to NULL */
429 1658 : if( !poFeature->IsFieldSet( i ) )
430 : {
431 6 : if (FAILED(hr = fgdb_row.SetNull(wfield_name)))
432 : {
433 0 : GDBErr(hr, "Failed setting field to NULL.");
434 0 : return OGRERR_FAILURE;
435 : }
436 6 : continue;
437 : }
438 :
439 : /* Set the information using the appropriate FGDB function */
440 1652 : int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
441 1652 : const std::string & strFieldType = m_vOGRFieldToESRIFieldType[i];
442 :
443 1652 : if ( nOGRFieldType == OFTInteger )
444 : {
445 1304 : int fldvalue = poFeature->GetFieldAsInteger(i);
446 1304 : if( strFieldType == "esriFieldTypeInteger" )
447 1303 : hr = fgdb_row.SetInteger(wfield_name, fldvalue);
448 : else
449 : {
450 1 : if( fldvalue < -32768 || fldvalue > 32767 )
451 : {
452 : static int bHasWarned = FALSE;
453 0 : if( !bHasWarned )
454 : {
455 0 : bHasWarned = TRUE;
456 : CPLError(CE_Warning, CPLE_NotSupported,
457 : "Value %d for field %s does not fit into a short and will be clamped. "
458 : "This warning will not be emitted any more",
459 0 : fldvalue, field_name.c_str());
460 : }
461 0 : if( fldvalue < -32768 )
462 0 : fldvalue = -32768;
463 : else
464 0 : fldvalue = 32767;
465 : }
466 1 : hr = fgdb_row.SetShort(wfield_name, (short) fldvalue);
467 : }
468 : }
469 348 : else if ( nOGRFieldType == OFTReal )
470 : {
471 : /* Doubles (we don't handle FGDB Floats) */
472 183 : double fldvalue = poFeature->GetFieldAsDouble(i);
473 183 : if( strFieldType == "esriFieldTypeDouble" )
474 182 : hr = fgdb_row.SetDouble(wfield_name, fldvalue);
475 : else
476 1 : hr = fgdb_row.SetFloat(wfield_name, (float) fldvalue);
477 : }
478 165 : else if ( nOGRFieldType == OFTString )
479 : {
480 : /* Strings we convert to wstring */
481 164 : std::string fldvalue = poFeature->GetFieldAsString(i);
482 164 : std::wstring wfldvalue = StringToWString(fldvalue);
483 164 : if( strFieldType == "esriFieldTypeString" )
484 162 : hr = fgdb_row.SetString(wfield_name, wfldvalue);
485 : // Apparently, esriFieldTypeGlobalID can not be set, but is
486 : // initialized by the FileGDB SDK itself.
487 2 : else if( strFieldType == "esriFieldTypeGUID" /*||
488 : strFieldType == "esriFieldTypeGlobalID" */ )
489 : {
490 1 : Guid guid;
491 1 : if( FAILED(hr = guid.FromString(wfldvalue)) )
492 : {
493 : CPLError( CE_Failure, CPLE_AppDefined,
494 : "Cannot parse GUID value %s for field %s.",
495 0 : fldvalue.c_str(), field_name.c_str() );
496 : }
497 : else
498 : {
499 1 : hr = fgdb_row.SetGUID(wfield_name, guid);
500 1 : }
501 : }
502 : else
503 1 : hr = 0;
504 : }
505 2 : else if ( nOGRFieldType == OFTDateTime || nOGRFieldType == OFTDate )
506 : {
507 : /* Dates we need to coerce a little */
508 : struct tm val;
509 : poFeature->GetFieldAsDateTime(i, &(val.tm_year), &(val.tm_mon), &(val.tm_mday),
510 1 : &(val.tm_hour), &(val.tm_min), &(val.tm_sec), NULL);
511 1 : val.tm_year -= 1900;
512 1 : val.tm_mon = val.tm_mon - 1; /* OGR months go 1-12, FGDB go 0-11 */
513 1 : hr = fgdb_row.SetDate(wfield_name, val);
514 : }
515 0 : else if ( nOGRFieldType == OFTBinary )
516 : {
517 : /* Binary data */
518 0 : ByteArray fgdb_bytearray;
519 : int bytesize;
520 0 : GByte *bytes = poFeature->GetFieldAsBinary(i, &bytesize);
521 0 : if ( bytesize )
522 : {
523 0 : fgdb_bytearray.Allocate(bytesize);
524 0 : memcpy(fgdb_bytearray.byteArray, bytes, bytesize);
525 0 : fgdb_bytearray.inUseLength = bytesize;
526 0 : hr = fgdb_row.SetBinary(wfield_name, fgdb_bytearray);
527 : }
528 : else
529 : {
530 0 : hr = fgdb_row.SetNull(wfield_name);
531 0 : }
532 : }
533 : else
534 : {
535 : /* We can't handle this type */
536 : CPLError( CE_Failure, CPLE_AppDefined,
537 0 : "FGDB driver does not support OGR type." );
538 0 : return OGRERR_FAILURE;
539 : }
540 :
541 1652 : if (FAILED(hr))
542 : {
543 : CPLError(CE_Warning, CPLE_AppDefined, "Cannot set value for field %s",
544 0 : field_name.c_str());
545 : }
546 : }
547 :
548 1167 : if ( m_pFeatureDefn->GetGeomType() != wkbNone )
549 : {
550 : /* Done with attribute fields, now do geometry */
551 1155 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
552 :
553 1155 : if (poGeom == NULL || poGeom->IsEmpty())
554 : {
555 : /* EMPTY geometries should be treated as NULL, see #4832 */
556 20 : hr = fgdb_row.SetNull(StringToWString(m_strShapeFieldName));
557 20 : if (FAILED(hr))
558 : {
559 0 : GDBErr(hr, "Failed at writing EMPTY Geometry to Row in CreateFeature.");
560 0 : return OGRERR_FAILURE;
561 : }
562 : }
563 : else
564 : {
565 : /* Write geometry to a buffer */
566 1135 : GByte *pabyShape = NULL;
567 1135 : int nShapeSize = 0;
568 1135 : OGRErr err = OGRWriteToShapeBin( poGeom, &pabyShape, &nShapeSize );
569 1135 : if ( err != OGRERR_NONE )
570 0 : return err;
571 :
572 : /* Copy it into a ShapeBuffer */
573 1135 : if ( nShapeSize > 0 )
574 : {
575 1135 : shape.Allocate(nShapeSize);
576 1135 : memcpy(shape.shapeBuffer, pabyShape, nShapeSize);
577 1135 : shape.inUseLength = nShapeSize;
578 : }
579 :
580 : /* Free the shape buffer */
581 1135 : CPLFree(pabyShape);
582 :
583 : /* Write ShapeBuffer into the Row */
584 1135 : hr = fgdb_row.SetGeometry(shape);
585 1135 : if (FAILED(hr))
586 : {
587 0 : GDBErr(hr, "Failed at writing Geometry to Row in CreateFeature.");
588 0 : return OGRERR_FAILURE;
589 : }
590 : }
591 : }
592 :
593 1167 : return OGRERR_NONE;
594 :
595 : }
596 :
597 : /************************************************************************/
598 : /* GetRow() */
599 : /************************************************************************/
600 :
601 193 : OGRErr FGdbLayer::GetRow( EnumRows& enumRows, Row& row, long nFID )
602 : {
603 : long hr;
604 193 : CPLString osQuery;
605 :
606 193 : osQuery.Printf("%s = %ld", m_strOIDFieldName.c_str(), nFID);
607 :
608 193 : if (FAILED(hr = m_pTable->Search(m_wstrSubfields, StringToWString(osQuery.c_str()), true, enumRows)))
609 : {
610 0 : GDBErr(hr, "Failed fetching row ");
611 0 : return OGRERR_FAILURE;
612 : }
613 :
614 193 : if (FAILED(hr = enumRows.Next(row)))
615 : {
616 0 : GDBErr(hr, "Failed fetching row ");
617 0 : return OGRERR_FAILURE;
618 : }
619 :
620 193 : if (hr != S_OK)
621 48 : return OGRERR_FAILURE; //none found - but no failure
622 :
623 145 : return OGRERR_NONE;
624 : }
625 :
626 : /************************************************************************/
627 : /* DeleteFeature() */
628 : /************************************************************************/
629 :
630 16 : OGRErr FGdbLayer::DeleteFeature( long nFID )
631 :
632 : {
633 : long hr;
634 16 : EnumRows enumRows;
635 16 : Row row;
636 :
637 16 : EndBulkLoad();
638 :
639 16 : if (GetRow(enumRows, row, nFID) != OGRERR_NONE)
640 0 : return OGRERR_FAILURE;
641 :
642 16 : if (FAILED(hr = m_pTable->Delete(row)))
643 : {
644 0 : GDBErr(hr, "Failed deleting row ");
645 0 : return OGRERR_FAILURE;
646 : }
647 :
648 16 : return OGRERR_NONE;
649 : }
650 :
651 : /************************************************************************/
652 : /* SetFeature() */
653 : /************************************************************************/
654 :
655 65 : OGRErr FGdbLayer::SetFeature( OGRFeature* poFeature )
656 :
657 : {
658 : long hr;
659 65 : EnumRows enumRows;
660 65 : Row row;
661 :
662 65 : if( poFeature->GetFID() == OGRNullFID )
663 : {
664 : CPLError( CE_Failure, CPLE_AppDefined,
665 0 : "SetFeature() with unset FID fails." );
666 0 : return OGRERR_FAILURE;
667 : }
668 :
669 65 : EndBulkLoad();
670 :
671 65 : if (GetRow(enumRows, row, poFeature->GetFID()) != OGRERR_NONE)
672 0 : return OGRERR_FAILURE;
673 :
674 : /* Populate the row with the feature content */
675 65 : if (PopulateRowWithFeature(row, poFeature) != OGRERR_NONE)
676 0 : return OGRERR_FAILURE;
677 :
678 65 : if (FAILED(hr = m_pTable->Update(row)))
679 : {
680 0 : GDBErr(hr, "Failed updating row ");
681 0 : return OGRERR_FAILURE;
682 : }
683 :
684 65 : return OGRERR_NONE;
685 : }
686 :
687 : /************************************************************************/
688 : /* CreateFieldDefn() */
689 : /************************************************************************/
690 :
691 73 : char* FGdbLayer::CreateFieldDefn(OGRFieldDefn& oField,
692 : int bApproxOK,
693 : std::string& fieldname_clean,
694 : std::string& gdbFieldType)
695 : {
696 73 : std::string fieldname = oField.GetNameRef();
697 146 : std::string fidname = std::string(GetFIDColumn());
698 146 : std::string nullable = "true";
699 :
700 : /* Try to map the OGR type to an ESRI type */
701 146 : OGRFieldType fldtype = oField.GetType();
702 73 : if ( ! OGRToGDBFieldType(fldtype, &gdbFieldType) )
703 : {
704 0 : GDBErr(-1, "Failed converting field type.");
705 0 : return NULL;
706 : }
707 :
708 73 : if (fieldname_clean.size() != 0)
709 : {
710 0 : oField.SetName(fieldname_clean.c_str());
711 : }
712 : else
713 : {
714 : /* Clean field names */
715 73 : fieldname_clean = FGDBLaunderName(fieldname);
716 :
717 73 : if (m_bLaunderReservedKeywords)
718 73 : fieldname_clean = FGDBEscapeReservedKeywords(fieldname_clean);
719 :
720 : /* Truncate to 64 characters */
721 73 : if (fieldname_clean.size() > 64)
722 2 : fieldname_clean.resize(64);
723 :
724 73 : std::string temp_fieldname = fieldname_clean;
725 :
726 : /* Ensures uniqueness of field name */
727 73 : int numRenames = 1;
728 149 : while ((m_pFeatureDefn->GetFieldIndex(temp_fieldname.c_str()) >= 0) && (numRenames < 10))
729 : {
730 3 : temp_fieldname = CPLSPrintf("%s_%d", fieldname_clean.substr(0, 62).c_str(), numRenames);
731 3 : numRenames ++;
732 : }
733 146 : while ((m_pFeatureDefn->GetFieldIndex(temp_fieldname.c_str()) >= 0) && (numRenames < 100))
734 : {
735 0 : temp_fieldname = CPLSPrintf("%s_%d", fieldname_clean.substr(0, 61).c_str(), numRenames);
736 0 : numRenames ++;
737 : }
738 :
739 73 : if (temp_fieldname != fieldname)
740 : {
741 5 : if( !bApproxOK || (m_pFeatureDefn->GetFieldIndex(temp_fieldname.c_str()) >= 0) )
742 : {
743 : CPLError( CE_Failure, CPLE_NotSupported,
744 : "Failed to add field named '%s'",
745 0 : fieldname.c_str() );
746 0 : return NULL;
747 : }
748 : CPLError(CE_Warning, CPLE_NotSupported,
749 : "Normalized/laundered field name: '%s' to '%s'",
750 5 : fieldname.c_str(), temp_fieldname.c_str());
751 :
752 5 : fieldname_clean = temp_fieldname;
753 5 : oField.SetName(fieldname_clean.c_str());
754 0 : }
755 : }
756 :
757 : /* Then the Field definition */
758 73 : CPLXMLNode *defn_xml = CPLCreateXMLNode(NULL, CXT_Element, "esri:Field");
759 :
760 : /* Add the XML attributes to the Field node */
761 73 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
762 73 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xs", "http://www.w3.org/2001/XMLSchema");
763 73 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:esri", "http://www.esri.com/schemas/ArcGIS/10.1");
764 73 : FGDB_CPLAddXMLAttribute(defn_xml, "xsi:type", "esri:Field");
765 :
766 : /* Basic field information */
767 73 : CPLCreateXMLElementAndValue(defn_xml, "Name", fieldname_clean.c_str());
768 73 : CPLCreateXMLElementAndValue(defn_xml, "Type", gdbFieldType.c_str());
769 73 : CPLCreateXMLElementAndValue(defn_xml, "IsNullable", nullable.c_str());
770 :
771 : /* Get the Width and Precision if we know them */
772 73 : int width = oField.GetWidth();
773 73 : int precision = oField.GetPrecision();
774 73 : if ( width <= 0 )
775 70 : GDBFieldTypeToWidthPrecision(gdbFieldType, &width, &precision);
776 :
777 : /* Write out the Width and Precision */
778 : char buf[100];
779 73 : snprintf(buf, 100, "%d", width);
780 73 : CPLCreateXMLElementAndValue(defn_xml,"Length", buf);
781 73 : snprintf(buf, 100, "%d", precision);
782 73 : CPLCreateXMLElementAndValue(defn_xml,"Precision", buf);
783 :
784 : /* We know nothing about Scale, so zero it out */
785 73 : CPLCreateXMLElementAndValue(defn_xml,"Scale", "0");
786 :
787 : /* Attempt to preserve the original fieldname */
788 73 : if (fieldname != fieldname_clean)
789 : {
790 5 : CPLCreateXMLElementAndValue(defn_xml, "AliasName", fieldname.c_str());
791 : }
792 :
793 : /* Default values are discouraged in OGR API docs */
794 : /* <DefaultValue xsi:type="xs:string">afternoon</DefaultValue> */
795 :
796 : /* Convert our XML tree into a string for FGDB */
797 73 : char *defn_str = CPLSerializeXMLTree(defn_xml);
798 73 : CPLDebug("FGDB", "CreateField() generated XML for FGDB\n%s", defn_str);
799 :
800 : /* Free the XML */
801 73 : CPLDestroyXMLNode(defn_xml);
802 :
803 73 : return defn_str;
804 : }
805 :
806 : /************************************************************************/
807 : /* CreateField() */
808 : /* Build up an FGDB XML field definition and use it to create a Field */
809 : /* Update the OGRFeatureDefn to reflect the new field. */
810 : /* */
811 : /************************************************************************/
812 :
813 73 : OGRErr FGdbLayer::CreateField(OGRFieldDefn* poField, int bApproxOK)
814 : {
815 73 : OGRFieldDefn oField(poField);
816 73 : std::string fieldname_clean;
817 73 : std::string gdbFieldType;
818 :
819 : char* defn_str = CreateFieldDefn(oField, bApproxOK,
820 73 : fieldname_clean, gdbFieldType);
821 73 : if (defn_str == NULL)
822 0 : return OGRERR_FAILURE;
823 :
824 : /* Add the FGDB Field to the FGDB Table. */
825 73 : fgdbError hr = m_pTable->AddField(defn_str);
826 :
827 73 : CPLFree(defn_str);
828 :
829 : /* Check the status of the Field add */
830 73 : if (FAILED(hr))
831 : {
832 0 : GDBErr(hr, "Failed at creating Field for " + std::string(oField.GetNameRef()));
833 0 : return OGRERR_FAILURE;
834 : }
835 :
836 : /* Now add the OGRFieldDefn to the OGRFeatureDefn */
837 73 : m_pFeatureDefn->AddFieldDefn(&oField);
838 :
839 73 : m_vOGRFieldToESRIField.push_back(StringToWString(fieldname_clean));
840 73 : m_vOGRFieldToESRIFieldType.push_back( gdbFieldType );
841 :
842 : /* All done and happy */
843 73 : return OGRERR_NONE;
844 :
845 : }
846 :
847 : /************************************************************************/
848 : /* DeleteField() */
849 : /************************************************************************/
850 :
851 1 : OGRErr FGdbLayer::DeleteField( int iFieldToDelete )
852 : {
853 1 : if (iFieldToDelete < 0 || iFieldToDelete >= m_pFeatureDefn->GetFieldCount())
854 : {
855 : CPLError( CE_Failure, CPLE_NotSupported,
856 0 : "Invalid field index");
857 0 : return OGRERR_FAILURE;
858 : }
859 :
860 1 : ResetReading();
861 :
862 1 : const char* pszFieldName = m_pFeatureDefn->GetFieldDefn(iFieldToDelete)->GetNameRef();
863 :
864 : fgdbError hr;
865 1 : if (FAILED(hr = m_pTable->DeleteField(StringToWString(pszFieldName))))
866 : {
867 0 : GDBErr(hr, "Failed deleting field " + std::string(pszFieldName));
868 0 : return OGRERR_FAILURE;
869 : }
870 :
871 1 : m_vOGRFieldToESRIField.erase (m_vOGRFieldToESRIField.begin() + iFieldToDelete);
872 1 : m_vOGRFieldToESRIFieldType.erase( m_vOGRFieldToESRIFieldType.begin() + iFieldToDelete );
873 :
874 :
875 1 : return m_pFeatureDefn->DeleteFieldDefn( iFieldToDelete );
876 : }
877 :
878 : #ifdef AlterFieldDefn_implemented_but_not_working
879 :
880 : /************************************************************************/
881 : /* AlterFieldDefn() */
882 : /************************************************************************/
883 :
884 : OGRErr FGdbLayer::AlterFieldDefn( int iFieldToAlter, OGRFieldDefn* poNewFieldDefn, int nFlags )
885 : {
886 : if (iFieldToAlter < 0 || iFieldToAlter >= m_pFeatureDefn->GetFieldCount())
887 : {
888 : CPLError( CE_Failure, CPLE_NotSupported,
889 : "Invalid field index");
890 : return OGRERR_FAILURE;
891 : }
892 :
893 : OGRFieldDefn* poFieldDefn = m_pFeatureDefn->GetFieldDefn(iFieldToAlter);
894 : OGRFieldDefn oField(poFieldDefn);
895 :
896 : if (nFlags & ALTER_TYPE_FLAG)
897 : oField.SetType(poNewFieldDefn->GetType());
898 : if (nFlags & ALTER_NAME_FLAG)
899 : {
900 : if (strcmp(poNewFieldDefn->GetNameRef(), oField.GetNameRef()) != 0)
901 : {
902 : CPLError( CE_Failure, CPLE_NotSupported,
903 : "Altering field name is not supported" );
904 : return OGRERR_FAILURE;
905 : }
906 : oField.SetName(poNewFieldDefn->GetNameRef());
907 : }
908 : if (nFlags & ALTER_WIDTH_PRECISION_FLAG)
909 : {
910 : oField.SetWidth(poNewFieldDefn->GetWidth());
911 : oField.SetPrecision(poNewFieldDefn->GetPrecision());
912 : }
913 :
914 : std::string fieldname_clean = WStringToString(m_vOGRFieldToESRIField[iFieldToAlter]);
915 : std::string gdbFieldType;
916 :
917 : char* defn_str = CreateFieldDefn(oField, TRUE,
918 : fieldname_clean, gdbFieldType);
919 : if (defn_str == NULL)
920 : return OGRERR_FAILURE;
921 :
922 : ResetReading();
923 :
924 : /* Add the FGDB Field to the FGDB Table. */
925 : fgdbError hr = m_pTable->AlterField(defn_str);
926 :
927 : CPLFree(defn_str);
928 :
929 : /* Check the status of the AlterField */
930 : if (FAILED(hr))
931 : {
932 : GDBErr(hr, "Failed at altering field " + std::string(oField.GetNameRef()));
933 : return OGRERR_FAILURE;
934 : }
935 :
936 : m_vOGRFieldToESRIFieldType[iFieldToAlter] = gdbFieldType;
937 :
938 : poFieldDefn->SetType(oField.GetType());
939 : poFieldDefn->SetWidth(oField.GetWidth());
940 : poFieldDefn->SetPrecision(oField.GetPrecision());
941 :
942 : return OGRERR_NONE;
943 : }
944 : #endif // AlterFieldDefn_implemented_but_not_working
945 :
946 : /************************************************************************/
947 : /* XMLSpatialReference() */
948 : /* Build up an XML representation of an OGRSpatialReference. */
949 : /* Used in layer creation. */
950 : /* */
951 : /************************************************************************/
952 :
953 37 : CPLXMLNode* XMLSpatialReference(OGRSpatialReference* poSRS, char** papszOptions)
954 : {
955 : /* We always need a SpatialReference */
956 37 : CPLXMLNode *srs_xml = CPLCreateXMLNode(NULL, CXT_Element, "SpatialReference");
957 :
958 : /* Extract the WKID before morphing */
959 37 : int nSRID = 0;
960 37 : if ( poSRS && poSRS->GetAuthorityCode(NULL) )
961 : {
962 31 : nSRID = atoi(poSRS->GetAuthorityCode(NULL));
963 : }
964 :
965 : /* NULL poSRS => UnknownCoordinateSystem */
966 37 : if ( ! poSRS )
967 : {
968 1 : FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:UnknownCoordinateSystem");
969 : }
970 : else
971 : {
972 : /* Set the SpatialReference type attribute correctly for GEOGCS/PROJCS */
973 36 : if ( poSRS->IsProjected() )
974 3 : FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:ProjectedCoordinateSystem");
975 : else
976 33 : FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:GeographicCoordinateSystem");
977 :
978 : /* Add the WKT to the XML */
979 36 : SpatialReferenceInfo oESRI_SRS;
980 :
981 : /* Do we have a known SRID ? If so, directly query the ESRI SRS DB */
982 36 : if( nSRID && SpatialReferences::FindSpatialReferenceBySRID(nSRID, oESRI_SRS) )
983 : {
984 : CPLDebug("FGDB",
985 : "Layer SRS has a SRID (%d). Using WKT from ESRI SRS DBFound perfect match. ",
986 30 : nSRID);
987 30 : CPLCreateXMLElementAndValue(srs_xml,"WKT", WStringToString(oESRI_SRS.srtext).c_str());
988 : }
989 : else
990 : {
991 : /* Make a clone so we can morph it without morphing the original */
992 6 : OGRSpatialReference* poSRSClone = poSRS->Clone();
993 :
994 : /* Flip the WKT to ESRI form, return UnknownCoordinateSystem if we can't */
995 6 : if ( poSRSClone->morphToESRI() != OGRERR_NONE )
996 : {
997 0 : delete poSRSClone;
998 0 : FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:UnknownCoordinateSystem");
999 0 : return srs_xml;
1000 : }
1001 :
1002 6 : char *wkt = NULL;
1003 6 : poSRSClone->exportToWkt(&wkt);
1004 6 : if (wkt)
1005 : {
1006 6 : EnumSpatialReferenceInfo oEnumESRI_SRS;
1007 6 : std::vector<int> oaiCandidateSRS;
1008 6 : nSRID = 0;
1009 :
1010 : /* Enumerate SRS from ESRI DB and find a match */
1011 11168 : while(TRUE)
1012 : {
1013 11174 : if ( poSRS->IsProjected() )
1014 : {
1015 8494 : if( !oEnumESRI_SRS.NextProjectedSpatialReference(oESRI_SRS) )
1016 2 : break;
1017 : }
1018 : else
1019 : {
1020 2680 : if( !oEnumESRI_SRS.NextGeographicSpatialReference(oESRI_SRS) )
1021 4 : break;
1022 : }
1023 :
1024 11168 : std::string osESRI_WKT = WStringToString(oESRI_SRS.srtext);
1025 11168 : const char* pszESRI_WKT = osESRI_WKT.c_str();
1026 11168 : if( strcmp(pszESRI_WKT, wkt) == 0 )
1027 : {
1028 : /* Exact match found (not sure this case happens) */
1029 0 : nSRID = oESRI_SRS.auth_srid;
1030 : break;
1031 : }
1032 11168 : OGRSpatialReference oSRS_FromESRI;
1033 11168 : if( oSRS_FromESRI.SetFromUserInput(pszESRI_WKT) == OGRERR_NONE &&
1034 : poSRSClone->IsSame(&oSRS_FromESRI) )
1035 : {
1036 : /* Potential match found */
1037 3 : oaiCandidateSRS.push_back(oESRI_SRS.auth_srid);
1038 : }
1039 : }
1040 :
1041 6 : if( nSRID != 0 )
1042 : {
1043 : CPLDebug("FGDB",
1044 : "Found perfect match in ESRI SRS DB "
1045 0 : "for layer SRS. SRID is %d", nSRID);
1046 : }
1047 6 : else if( oaiCandidateSRS.size() == 0 )
1048 : {
1049 : CPLDebug("FGDB",
1050 : "Did not found a match in ESRI SRS DB for layer SRS. "
1051 3 : "Using morphed SRS WKT. Failure is to be expected");
1052 : }
1053 3 : else if( oaiCandidateSRS.size() == 1 )
1054 : {
1055 3 : nSRID = oaiCandidateSRS[0];
1056 3 : if( SpatialReferences::FindSpatialReferenceBySRID(
1057 : nSRID, oESRI_SRS) )
1058 : {
1059 : CPLDebug("FGDB",
1060 : "Found a single match in ESRI SRS DB "
1061 : "for layer SRS. SRID is %d",
1062 3 : nSRID);
1063 3 : nSRID = oESRI_SRS.auth_srid;
1064 3 : OGRFree(wkt);
1065 3 : wkt = CPLStrdup(WStringToString(oESRI_SRS.srtext).c_str());
1066 : }
1067 : }
1068 : else
1069 : {
1070 : /* Not sure this case can happen */
1071 :
1072 0 : CPLString osCandidateSRS;
1073 0 : for(int i=0; i<(int)oaiCandidateSRS.size() && i < 10; i++)
1074 : {
1075 0 : if( osCandidateSRS.size() )
1076 0 : osCandidateSRS += ", ";
1077 0 : osCandidateSRS += CPLSPrintf("%d", oaiCandidateSRS[i]);
1078 : }
1079 0 : if(oaiCandidateSRS.size() > 10)
1080 0 : osCandidateSRS += "...";
1081 :
1082 : CPLDebug("FGDB",
1083 : "As several candidates (%s) have been found in "
1084 : "ESRI SRS DB for layer SRS, none has been selected. "
1085 : "Using morphed SRS WKT. Failure is to be expected",
1086 0 : osCandidateSRS.c_str());
1087 : }
1088 :
1089 6 : CPLCreateXMLElementAndValue(srs_xml,"WKT", wkt);
1090 6 : OGRFree(wkt);
1091 : }
1092 :
1093 : /* Dispose of our close */
1094 6 : delete poSRSClone;
1095 0 : }
1096 : }
1097 :
1098 : /* Handle Origin/Scale/Tolerance */
1099 : const char* grid[7] = {
1100 : "XOrigin", "YOrigin", "XYScale",
1101 : "ZOrigin", "ZScale",
1102 37 : "XYTolerance", "ZTolerance" };
1103 : const char* gridvalues[7];
1104 :
1105 : /*
1106 : Need different default paramters for geographic and projected coordinate systems.
1107 : Try and use ArcGIS 10 default values.
1108 : */
1109 : // default tolerance is 1mm in the units of the coordinate system
1110 37 : double ztol = 0.001 * (poSRS ? poSRS->GetTargetLinearUnits("VERT_CS") : 1.0);
1111 : // default scale is 10x the tolerance
1112 37 : long zscale = 1 / ztol * 10;
1113 :
1114 : char s_xyscale[50], s_xytol[50], s_zscale[50], s_ztol[50];
1115 37 : snprintf(s_ztol, 50, "%f", ztol);
1116 37 : snprintf(s_zscale, 50, "%ld", zscale);
1117 :
1118 37 : if ( poSRS == NULL || poSRS->IsProjected() )
1119 : {
1120 : // default tolerance is 1mm in the units of the coordinate system
1121 4 : double xytol = 0.001 * (poSRS ? poSRS->GetTargetLinearUnits("PROJCS") : 1.0);
1122 : // default scale is 10x the tolerance
1123 4 : long xyscale = 1 / xytol * 10;
1124 :
1125 4 : snprintf(s_xytol, 50, "%f", xytol);
1126 4 : snprintf(s_xyscale, 50, "%ld", xyscale);
1127 :
1128 : // Ideally we would use the same X/Y origins as ArcGIS, but we need the algorithm they use.
1129 4 : gridvalues[0] = "-2147483647";
1130 4 : gridvalues[1] = "-2147483647";
1131 4 : gridvalues[2] = s_xyscale;
1132 4 : gridvalues[3] = "-100000";
1133 4 : gridvalues[4] = s_zscale;
1134 4 : gridvalues[5] = s_xytol;
1135 4 : gridvalues[6] = s_ztol;
1136 : }
1137 : else
1138 : {
1139 33 : gridvalues[0] = "-400";
1140 33 : gridvalues[1] = "-400";
1141 33 : gridvalues[2] = "1000000000";
1142 33 : gridvalues[3] = "-100000";
1143 33 : gridvalues[4] = s_zscale;
1144 33 : gridvalues[5] = "0.000000008983153";
1145 33 : gridvalues[6] = s_ztol;
1146 : }
1147 :
1148 : /* Convert any layer creation options available, use defaults otherwise */
1149 296 : for( int i = 0; i < 7; i++ )
1150 : {
1151 259 : if ( CSLFetchNameValue( papszOptions, grid[i] ) != NULL )
1152 0 : gridvalues[i] = CSLFetchNameValue( papszOptions, grid[i] );
1153 :
1154 259 : CPLCreateXMLElementAndValue(srs_xml, grid[i], gridvalues[i]);
1155 : }
1156 :
1157 : /* FGDB is always High Precision */
1158 37 : CPLCreateXMLElementAndValue(srs_xml, "HighPrecision", "true");
1159 :
1160 : /* Add the WKID to the XML */
1161 37 : if ( nSRID )
1162 : {
1163 33 : CPLCreateXMLElementAndValue(srs_xml, "WKID", CPLSPrintf("%d", nSRID));
1164 : }
1165 :
1166 37 : return srs_xml;
1167 : }
1168 :
1169 : /************************************************************************/
1170 : /* CreateFeatureDataset() */
1171 : /************************************************************************/
1172 :
1173 1 : bool FGdbLayer::CreateFeatureDataset(FGdbDataSource* pParentDataSource,
1174 : std::string feature_dataset_name,
1175 : OGRSpatialReference* poSRS,
1176 : char** papszOptions )
1177 : {
1178 : /* XML node */
1179 1 : CPLXMLNode *xml_xml = CPLCreateXMLNode(NULL, CXT_Element, "?xml");
1180 1 : FGDB_CPLAddXMLAttribute(xml_xml, "version", "1.0");
1181 1 : FGDB_CPLAddXMLAttribute(xml_xml, "encoding", "UTF-8");
1182 :
1183 : /* First build up a bare-bones feature definition */
1184 1 : CPLXMLNode *defn_xml = CPLCreateXMLNode(NULL, CXT_Element, "esri:DataElement");
1185 1 : CPLAddXMLSibling(xml_xml, defn_xml);
1186 :
1187 : /* Add the attributes to the DataElement */
1188 1 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
1189 1 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xs", "http://www.w3.org/2001/XMLSchema");
1190 1 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:esri", "http://www.esri.com/schemas/ArcGIS/10.1");
1191 :
1192 : /* Need to set this to esri:DEFeatureDataset or esri:DETable */
1193 1 : FGDB_CPLAddXMLAttribute(defn_xml, "xsi:type", "esri:DEFeatureDataset");
1194 :
1195 : /* Add in more children */
1196 1 : std::string catalog_page = "\\" + feature_dataset_name;
1197 1 : CPLCreateXMLElementAndValue(defn_xml,"CatalogPath", catalog_page.c_str());
1198 1 : CPLCreateXMLElementAndValue(defn_xml,"Name", feature_dataset_name.c_str());
1199 1 : CPLCreateXMLElementAndValue(defn_xml,"ChildrenExpanded", "false");
1200 1 : CPLCreateXMLElementAndValue(defn_xml,"DatasetType", "esriDTFeatureDataset");
1201 1 : CPLCreateXMLElementAndValue(defn_xml,"Versioned", "false");
1202 1 : CPLCreateXMLElementAndValue(defn_xml,"CanVersion", "false");
1203 :
1204 : /* Add in empty extent */
1205 1 : CPLXMLNode *extent_xml = CPLCreateXMLNode(NULL, CXT_Element, "Extent");
1206 1 : FGDB_CPLAddXMLAttribute(extent_xml, "xsi:nil", "true");
1207 1 : CPLAddXMLChild(defn_xml, extent_xml);
1208 :
1209 : /* Add the SRS */
1210 : if( TRUE ) // TODO: conditional on existence of SRS
1211 : {
1212 1 : CPLXMLNode *srs_xml = XMLSpatialReference(poSRS, papszOptions);
1213 1 : if ( srs_xml )
1214 1 : CPLAddXMLChild(defn_xml, srs_xml);
1215 : }
1216 :
1217 : /* Convert our XML tree into a string for FGDB */
1218 1 : char *defn_str = CPLSerializeXMLTree(xml_xml);
1219 1 : CPLDestroyXMLNode(xml_xml);
1220 :
1221 : /* TODO, tie this to debugging levels */
1222 1 : CPLDebug("FGDB", "%s", defn_str);
1223 :
1224 : /* Create the FeatureDataset. */
1225 1 : Geodatabase *gdb = pParentDataSource->GetGDB();
1226 1 : fgdbError hr = gdb->CreateFeatureDataset(defn_str);
1227 :
1228 : /* Free the XML */
1229 1 : CPLFree(defn_str);
1230 :
1231 : /* Check table create status */
1232 1 : if (FAILED(hr))
1233 : {
1234 0 : return GDBErr(hr, "Failed at creating FeatureDataset " + feature_dataset_name);
1235 : }
1236 :
1237 1 : return true;
1238 : }
1239 :
1240 : /************************************************************************/
1241 : /* Create() */
1242 : /* Build up an FGDB XML layer definition and use it to create a Table */
1243 : /* or Feature Class to work from. */
1244 : /* */
1245 : /* Layer creation options: */
1246 : /* FEATURE_DATASET, nest layer inside a FeatureDataset folder */
1247 : /* GEOMETRY_NAME, user-selected name for the geometry column */
1248 : /* OID_NAME, user-selected name for the FID column */
1249 : /* XORIGIN, YORIGIN, ZORIGIN, origin of the snapping grid */
1250 : /* XYSCALE, ZSCALE, inverse resolution of the snapping grid */
1251 : /* XYTOLERANCE, ZTOLERANCE, snapping tolerance for topology/networks */
1252 : /* */
1253 : /************************************************************************/
1254 :
1255 38 : bool FGdbLayer::Create(FGdbDataSource* pParentDataSource,
1256 : const char* pszLayerNameIn,
1257 : OGRSpatialReference* poSRS,
1258 : OGRwkbGeometryType eType,
1259 : char** papszOptions)
1260 : {
1261 38 : std::string parent_path = "";
1262 76 : std::wstring wtable_path, wparent_path;
1263 38 : std::string geometry_name = FGDB_GEOMETRY_NAME;
1264 76 : std::string fid_name = FGDB_OID_NAME;
1265 38 : std::string esri_type;
1266 38 : bool has_z = false;
1267 :
1268 : #ifdef EXTENT_WORKAROUND
1269 38 : m_bLayerJustCreated = true;
1270 : #endif
1271 :
1272 : /* Launder the Layer name */
1273 38 : std::string layerName = pszLayerNameIn;
1274 :
1275 76 : layerName = FGDBLaunderName(pszLayerNameIn);
1276 76 : layerName = FGDBEscapeReservedKeywords(layerName);
1277 38 : layerName = FGDBEscapeUnsupportedPrefixes(layerName);
1278 :
1279 38 : if (layerName.size() > 160)
1280 2 : layerName.resize(160);
1281 :
1282 : /* Ensures uniqueness of layer name */
1283 38 : int numRenames = 1;
1284 79 : while ((pParentDataSource->GetLayerByName(layerName.c_str()) != NULL) && (numRenames < 10))
1285 : {
1286 3 : layerName = CPLSPrintf("%s_%d", layerName.substr(0, 158).c_str(), numRenames);
1287 3 : numRenames ++;
1288 : }
1289 76 : while ((pParentDataSource->GetLayerByName(layerName.c_str()) != NULL) && (numRenames < 100))
1290 : {
1291 0 : layerName = CPLSPrintf("%s_%d", layerName.substr(0, 157).c_str(), numRenames);
1292 0 : numRenames ++;
1293 : }
1294 :
1295 38 : if (layerName != pszLayerNameIn)
1296 : {
1297 : CPLError(CE_Warning, CPLE_NotSupported,
1298 : "Normalized/laundered layer name: '%s' to '%s'",
1299 6 : pszLayerNameIn, layerName.c_str());
1300 : }
1301 :
1302 38 : std::string table_path = "\\" + std::string(layerName);
1303 :
1304 : /* Handle the FEATURE_DATASET case */
1305 38 : if ( CSLFetchNameValue( papszOptions, "FEATURE_DATASET") != NULL )
1306 : {
1307 2 : std::string feature_dataset = CSLFetchNameValue( papszOptions, "FEATURE_DATASET");
1308 :
1309 : /* Check if FEATURE_DATASET exists. Otherwise create it */
1310 2 : std::vector<wstring> featuredatasets;
1311 2 : Geodatabase *gdb = pParentDataSource->GetGDB();
1312 2 : int bFeatureDataSetExists = FALSE;
1313 : fgdbError hr;
1314 2 : if ( !FAILED(hr = gdb->GetChildDatasets(L"\\", L"Feature Dataset", featuredatasets)) )
1315 : {
1316 2 : std::wstring feature_dataset_with_slash = L"\\" + StringToWString(feature_dataset);
1317 3 : for ( unsigned int i = 0; i < featuredatasets.size(); i++ )
1318 : {
1319 1 : if (featuredatasets[i] == feature_dataset_with_slash)
1320 1 : bFeatureDataSetExists = TRUE;
1321 2 : }
1322 : }
1323 :
1324 2 : if (!bFeatureDataSetExists)
1325 : {
1326 1 : bool rv = CreateFeatureDataset(pParentDataSource, feature_dataset, poSRS, papszOptions);
1327 1 : if ( ! rv )
1328 0 : return rv;
1329 : }
1330 :
1331 2 : table_path = "\\" + feature_dataset + table_path;
1332 2 : parent_path = "\\" + feature_dataset;
1333 : }
1334 :
1335 : /* Convert table_path into wstring */
1336 38 : wtable_path = StringToWString(table_path);
1337 38 : wparent_path = StringToWString(parent_path);
1338 :
1339 : /* Over-ride the geometry name if necessary */
1340 38 : if ( CSLFetchNameValue( papszOptions, "GEOMETRY_NAME") != NULL )
1341 0 : geometry_name = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME");
1342 :
1343 : /* Over-ride the OID name if necessary */
1344 38 : if ( CSLFetchNameValue( papszOptions, "OID_NAME") != NULL )
1345 0 : fid_name = CSLFetchNameValue( papszOptions, "OID_NAME");
1346 :
1347 : /* Figure out our geometry type */
1348 38 : if ( eType != wkbNone )
1349 : {
1350 36 : if ( wkbFlatten(eType) == wkbUnknown )
1351 : {
1352 0 : return GDBErr(-1, "FGDB layers cannot be created with a wkbUnknown layer geometry type.");
1353 : }
1354 36 : if ( ! OGRGeometryToGDB(eType, &esri_type, &has_z) )
1355 0 : return GDBErr(-1, "Unable to map OGR type to ESRI type");
1356 : }
1357 :
1358 38 : m_bLaunderReservedKeywords = CSLFetchBoolean( papszOptions, "LAUNDER_RESERVED_KEYWORDS", TRUE) == TRUE;
1359 :
1360 : /* XML node */
1361 38 : CPLXMLNode *xml_xml = CPLCreateXMLNode(NULL, CXT_Element, "?xml");
1362 38 : FGDB_CPLAddXMLAttribute(xml_xml, "version", "1.0");
1363 38 : FGDB_CPLAddXMLAttribute(xml_xml, "encoding", "UTF-8");
1364 :
1365 : /* First build up a bare-bones feature definition */
1366 38 : CPLXMLNode *defn_xml = CPLCreateXMLNode(NULL, CXT_Element, "esri:DataElement");
1367 38 : CPLAddXMLSibling(xml_xml, defn_xml);
1368 :
1369 : /* Add the attributes to the DataElement */
1370 38 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
1371 38 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xs", "http://www.w3.org/2001/XMLSchema");
1372 38 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:esri", "http://www.esri.com/schemas/ArcGIS/10.1");
1373 :
1374 : /* Need to set this to esri:DEFeatureDataset or esri:DETable */
1375 38 : FGDB_CPLAddXMLAttribute(defn_xml, "xsi:type", (eType == wkbNone ? "esri:DETable" : "esri:DEFeatureClass"));
1376 :
1377 : /* Add in more children */
1378 38 : CPLCreateXMLElementAndValue(defn_xml,"CatalogPath",table_path.c_str());
1379 38 : CPLCreateXMLElementAndValue(defn_xml,"Name", layerName.c_str());
1380 38 : CPLCreateXMLElementAndValue(defn_xml,"ChildrenExpanded", "false");
1381 :
1382 : /* WKB type of none implies this is a 'Table' otherwise it's a 'Feature Class' */
1383 38 : std::string datasettype = (eType == wkbNone ? "esriDTTable" : "esriDTFeatureClass");
1384 76 : CPLCreateXMLElementAndValue(defn_xml,"DatasetType", datasettype.c_str() );
1385 38 : CPLCreateXMLElementAndValue(defn_xml,"Versioned", "false");
1386 38 : CPLCreateXMLElementAndValue(defn_xml,"CanVersion", "false");
1387 :
1388 : /* We might need to make OID optional later, but OGR likes to have a FID */
1389 38 : CPLCreateXMLElementAndValue(defn_xml,"HasOID", "true");
1390 38 : CPLCreateXMLElementAndValue(defn_xml,"OIDFieldName", fid_name.c_str());
1391 :
1392 : /* Add in empty Fields */
1393 38 : CPLXMLNode *fields_xml = CPLCreateXMLNode(defn_xml, CXT_Element, "Fields");
1394 38 : FGDB_CPLAddXMLAttribute(fields_xml, "xsi:type", "esri:Fields");
1395 38 : CPLXMLNode *fieldarray_xml = CPLCreateXMLNode(fields_xml, CXT_Element, "FieldArray");
1396 38 : FGDB_CPLAddXMLAttribute(fieldarray_xml, "xsi:type", "esri:ArrayOfField");
1397 :
1398 : /* Feature Classes have an implicit geometry column, so we'll add it at creation time */
1399 38 : CPLXMLNode *srs_xml = NULL;
1400 38 : if ( eType != wkbNone )
1401 : {
1402 36 : CPLXMLNode *shape_xml = CPLCreateXMLNode(fieldarray_xml, CXT_Element, "Field");
1403 36 : FGDB_CPLAddXMLAttribute(shape_xml, "xsi:type", "esri:Field");
1404 36 : CPLCreateXMLElementAndValue(shape_xml, "Name", geometry_name.c_str());
1405 36 : CPLCreateXMLElementAndValue(shape_xml, "Type", "esriFieldTypeGeometry");
1406 36 : CPLCreateXMLElementAndValue(shape_xml, "IsNullable", "true");
1407 36 : CPLCreateXMLElementAndValue(shape_xml, "Length", "0");
1408 36 : CPLCreateXMLElementAndValue(shape_xml, "Precision", "0");
1409 36 : CPLCreateXMLElementAndValue(shape_xml, "Scale", "0");
1410 36 : CPLCreateXMLElementAndValue(shape_xml, "Required", "true");
1411 36 : CPLXMLNode *geom_xml = CPLCreateXMLNode(shape_xml, CXT_Element, "GeometryDef");
1412 36 : FGDB_CPLAddXMLAttribute(geom_xml, "xsi:type", "esri:GeometryDef");
1413 36 : CPLCreateXMLElementAndValue(geom_xml, "AvgNumPoints", "0");
1414 36 : CPLCreateXMLElementAndValue(geom_xml, "GeometryType", esri_type.c_str());
1415 36 : CPLCreateXMLElementAndValue(geom_xml,"HasM", "false");
1416 36 : CPLCreateXMLElementAndValue(geom_xml,"HasZ", (has_z ? "true" : "false"));
1417 :
1418 : /* Add the SRS if we have one */
1419 36 : srs_xml = XMLSpatialReference(poSRS, papszOptions);
1420 36 : if ( srs_xml )
1421 36 : CPLAddXMLChild(geom_xml, srs_xml);
1422 : }
1423 :
1424 : /* All (?) Tables and Feature Classes will have an ObjectID */
1425 38 : CPLXMLNode *oid_xml = CPLCreateXMLNode(fieldarray_xml, CXT_Element, "Field");
1426 38 : FGDB_CPLAddXMLAttribute(oid_xml, "xsi:type", "esri:Field");
1427 38 : CPLCreateXMLElementAndValue(oid_xml, "Name", fid_name.c_str());
1428 38 : CPLCreateXMLElementAndValue(oid_xml, "Type", "esriFieldTypeOID");
1429 38 : CPLCreateXMLElementAndValue(oid_xml, "IsNullable", "false");
1430 38 : CPLCreateXMLElementAndValue(oid_xml, "Length", "12");
1431 38 : CPLCreateXMLElementAndValue(oid_xml, "Precision", "0");
1432 38 : CPLCreateXMLElementAndValue(oid_xml, "Scale", "0");
1433 38 : CPLCreateXMLElementAndValue(oid_xml, "Required", "true");
1434 :
1435 : /* Add in empty Indexes */
1436 38 : CPLXMLNode *indexes_xml = CPLCreateXMLNode(defn_xml, CXT_Element, "Indexes");
1437 38 : FGDB_CPLAddXMLAttribute(indexes_xml, "xsi:type", "esri:Indexes");
1438 38 : CPLXMLNode *indexarray_xml = CPLCreateXMLNode(indexes_xml, CXT_Element, "IndexArray");
1439 38 : FGDB_CPLAddXMLAttribute(indexarray_xml, "xsi:type", "esri:ArrayOfIndex");
1440 :
1441 : /* CLSID http://forums.arcgis.com/threads/34536?p=118484#post118484 */
1442 38 : if ( eType == wkbNone )
1443 : {
1444 2 : CPLCreateXMLElementAndValue(defn_xml, "CLSID", "{7A566981-C114-11D2-8A28-006097AFF44E}");
1445 2 : CPLCreateXMLElementAndValue(defn_xml, "EXTCLSID", "");
1446 : }
1447 : else
1448 : {
1449 36 : CPLCreateXMLElementAndValue(defn_xml, "CLSID", "{52353152-891A-11D0-BEC6-00805F7C4268}");
1450 36 : CPLCreateXMLElementAndValue(defn_xml, "EXTCLSID", "");
1451 : }
1452 :
1453 : /* Set the alias for the Feature Class */
1454 38 : if (pszLayerNameIn != layerName)
1455 : {
1456 6 : CPLCreateXMLElementAndValue(defn_xml, "AliasName", pszLayerNameIn);
1457 : }
1458 :
1459 : /* Map from OGR WKB type to ESRI type */
1460 38 : if ( eType != wkbNone )
1461 : {
1462 : /* Declare our feature type */
1463 36 : CPLCreateXMLElementAndValue(defn_xml,"FeatureType", "esriFTSimple");
1464 36 : CPLCreateXMLElementAndValue(defn_xml,"ShapeType", esri_type.c_str());
1465 36 : CPLCreateXMLElementAndValue(defn_xml,"ShapeFieldName", geometry_name.c_str());
1466 :
1467 : /* Dimensionality */
1468 36 : CPLCreateXMLElementAndValue(defn_xml,"HasM", "false");
1469 36 : CPLCreateXMLElementAndValue(defn_xml,"HasZ", (has_z ? "true" : "false"));
1470 :
1471 : /* TODO: Handle spatial indexes (layer creation option?) */
1472 36 : CPLCreateXMLElementAndValue(defn_xml,"HasSpatialIndex", "false");
1473 :
1474 : /* These field are required for Arcmap to display aliases correctly */
1475 36 : CPLCreateXMLNode(defn_xml, CXT_Element, "AreaFieldName");
1476 36 : CPLCreateXMLNode(defn_xml, CXT_Element, "LengthFieldName");
1477 :
1478 : /* We can't know the extent at this point <Extent xsi:nil='true'/> */
1479 36 : CPLXMLNode *extn_xml = CPLCreateXMLNode(defn_xml, CXT_Element, "Extent");
1480 36 : FGDB_CPLAddXMLAttribute(extn_xml, "xsi:nil", "true");
1481 : }
1482 :
1483 : /* Feature Class with known SRS gets an SRS entry */
1484 38 : if( eType != wkbNone && srs_xml != NULL)
1485 : {
1486 36 : CPLAddXMLChild(defn_xml, CPLCloneXMLTree(srs_xml));
1487 : }
1488 :
1489 : /* Convert our XML tree into a string for FGDB */
1490 : char *defn_str;
1491 :
1492 38 : if( CSLFetchNameValue( papszOptions, "XML_DEFINITION") != NULL )
1493 1 : defn_str = CPLStrdup(CSLFetchNameValue( papszOptions, "XML_DEFINITION"));
1494 : else
1495 37 : defn_str = CPLSerializeXMLTree(xml_xml);
1496 38 : CPLDestroyXMLNode(xml_xml);
1497 :
1498 : /* TODO, tie this to debugging levels */
1499 38 : CPLDebug("FGDB", "%s", defn_str);
1500 : //std::cout << defn_str << std::endl;
1501 :
1502 : /* Create the table. */
1503 38 : Table *table = new Table;
1504 76 : Geodatabase *gdb = pParentDataSource->GetGDB();
1505 38 : fgdbError hr = gdb->CreateTable(defn_str, wparent_path, *table);
1506 :
1507 : /* Free the XML */
1508 38 : CPLFree(defn_str);
1509 :
1510 : /* Check table create status */
1511 38 : if (FAILED(hr))
1512 : {
1513 3 : delete table;
1514 3 : return GDBErr(hr, "Failed at creating table for " + table_path);
1515 : }
1516 :
1517 : /* Store the new FGDB Table pointer and set up the OGRFeatureDefn */
1518 35 : return FGdbLayer::Initialize(pParentDataSource, table, wtable_path, L"Table");
1519 : }
1520 :
1521 : /*************************************************************************/
1522 : /* Initialize() */
1523 : /* Has ownership of the table as soon as it is called. */
1524 : /************************************************************************/
1525 :
1526 182 : bool FGdbLayer::Initialize(FGdbDataSource* pParentDataSource, Table* pTable,
1527 : std::wstring wstrTablePath, std::wstring wstrType)
1528 : {
1529 : long hr;
1530 :
1531 182 : m_pDS = pParentDataSource; // we never assume ownership of the parent - so our destructor should not delete
1532 :
1533 182 : m_pTable = pTable;
1534 :
1535 182 : m_wstrTablePath = wstrTablePath;
1536 182 : m_wstrType = wstrType;
1537 :
1538 182 : wstring wstrQueryName;
1539 182 : if (FAILED(hr = pParentDataSource->GetGDB()->GetQueryName(wstrTablePath, wstrQueryName)))
1540 : return GDBErr(hr, "Failed at getting underlying table name for " +
1541 0 : WStringToString(wstrTablePath));
1542 :
1543 182 : m_strName = WStringToString(wstrQueryName);
1544 :
1545 182 : m_pFeatureDefn = new OGRFeatureDefn(m_strName.c_str()); //TODO: Should I "new" an OGR smart pointer - sample says so, but it doesn't seem right
1546 : //as long as we use the same compiler & settings in both the ogr build and this
1547 : //driver, we should be OK
1548 182 : m_pFeatureDefn->Reference();
1549 :
1550 182 : string tableDef;
1551 182 : if (FAILED(hr = m_pTable->GetDefinition(tableDef)))
1552 : return GDBErr(hr, "Failed at getting table definition for " +
1553 0 : WStringToString(wstrTablePath));
1554 :
1555 : //xxx printf("Table definition = %s", tableDef.c_str() );
1556 :
1557 182 : bool abort = false;
1558 :
1559 : // extract schema information from table
1560 182 : CPLXMLNode *psRoot = CPLParseXMLString( tableDef.c_str() );
1561 :
1562 182 : if (psRoot == NULL)
1563 : {
1564 : CPLError( CE_Failure, CPLE_AppDefined, "%s",
1565 0 : ("Failed parsing GDB Table Schema XML for " + m_strName).c_str());
1566 0 : return false;
1567 : }
1568 :
1569 182 : CPLXMLNode *pDataElementNode = psRoot->psNext; // Move to next field which should be DataElement
1570 :
1571 182 : if( pDataElementNode != NULL
1572 : && pDataElementNode->psChild != NULL
1573 : && pDataElementNode->eType == CXT_Element
1574 : && EQUAL(pDataElementNode->pszValue,"esri:DataElement") )
1575 : {
1576 : CPLXMLNode *psNode;
1577 :
1578 4986 : for( psNode = pDataElementNode->psChild;
1579 : psNode != NULL;
1580 : psNode = psNode->psNext )
1581 : {
1582 4804 : if( psNode->eType == CXT_Element && psNode->psChild != NULL )
1583 : {
1584 3554 : if (EQUAL(psNode->pszValue,"OIDFieldName") )
1585 : {
1586 : char* pszUnescaped = CPLUnescapeString(
1587 182 : psNode->psChild->pszValue, NULL, CPLES_XML);
1588 182 : m_strOIDFieldName = pszUnescaped;
1589 182 : CPLFree(pszUnescaped);
1590 : }
1591 3372 : else if (EQUAL(psNode->pszValue,"ShapeFieldName") )
1592 : {
1593 : char* pszUnescaped = CPLUnescapeString(
1594 170 : psNode->psChild->pszValue, NULL, CPLES_XML);
1595 170 : m_strShapeFieldName = pszUnescaped;
1596 170 : CPLFree(pszUnescaped);
1597 : }
1598 3202 : else if (EQUAL(psNode->pszValue,"Fields") )
1599 : {
1600 182 : if (!GDBToOGRFields(psNode))
1601 : {
1602 0 : abort = true;
1603 0 : break;
1604 : }
1605 : }
1606 : }
1607 : }
1608 :
1609 182 : if (m_strShapeFieldName.size() == 0)
1610 12 : m_pFeatureDefn->SetGeomType(wkbNone);
1611 : }
1612 : else
1613 : {
1614 : CPLError( CE_Failure, CPLE_AppDefined, "%s",
1615 0 : ("Failed parsing GDB Table Schema XML (DataElement) for " + m_strName).c_str());
1616 0 : return false;
1617 : }
1618 182 : CPLDestroyXMLNode( psRoot );
1619 :
1620 182 : if (abort)
1621 0 : return false;
1622 :
1623 182 : return true; //AOToOGRFields(ipFields, m_pFeatureDefn, m_vOGRFieldToESRIField);
1624 : }
1625 :
1626 : /************************************************************************/
1627 : /* ParseGeometryDef() */
1628 : /************************************************************************/
1629 :
1630 170 : bool FGdbLayer::ParseGeometryDef(CPLXMLNode* psRoot)
1631 : {
1632 : CPLXMLNode *psGeometryDefItem;
1633 :
1634 170 : string geometryType;
1635 170 : bool hasZ = false;
1636 170 : string wkt, wkid;
1637 :
1638 1360 : for (psGeometryDefItem = psRoot->psChild;
1639 : psGeometryDefItem != NULL;
1640 : psGeometryDefItem = psGeometryDefItem->psNext )
1641 : {
1642 : //loop through all "GeometryDef" elements
1643 : //
1644 :
1645 1190 : if (psGeometryDefItem->eType == CXT_Element &&
1646 : psGeometryDefItem->psChild != NULL)
1647 : {
1648 1020 : if (EQUAL(psGeometryDefItem->pszValue,"GeometryType"))
1649 : {
1650 : char* pszUnescaped = CPLUnescapeString(
1651 170 : psGeometryDefItem->psChild->pszValue, NULL, CPLES_XML);
1652 :
1653 170 : geometryType = pszUnescaped;
1654 :
1655 170 : CPLFree(pszUnescaped);
1656 : }
1657 850 : else if (EQUAL(psGeometryDefItem->pszValue,"SpatialReference"))
1658 : {
1659 170 : ParseSpatialReference(psGeometryDefItem, &wkt, &wkid); // we don't check for success because it
1660 : // may not be there
1661 : }
1662 : /* No M support in OGR yet
1663 : else if (EQUAL(psFieldNode->pszValue,"HasM")
1664 : {
1665 : char* pszUnescaped = CPLUnescapeString(psNode->psChild->pszValue, NULL, CPLES_XML);
1666 :
1667 : if (!strcmp(szUnescaped, "true"))
1668 : hasM = true;
1669 :
1670 : CPLFree(pszUnescaped);
1671 : }
1672 : */
1673 680 : else if (EQUAL(psGeometryDefItem->pszValue,"HasZ"))
1674 : {
1675 : char* pszUnescaped = CPLUnescapeString(
1676 170 : psGeometryDefItem->psChild->pszValue, NULL, CPLES_XML);
1677 :
1678 170 : if (!strcmp(pszUnescaped, "true"))
1679 60 : hasZ = true;
1680 :
1681 170 : CPLFree(pszUnescaped);
1682 : }
1683 : }
1684 :
1685 : }
1686 :
1687 : OGRwkbGeometryType ogrGeoType;
1688 170 : if (!GDBToOGRGeometry(geometryType, hasZ, &ogrGeoType))
1689 0 : return false;
1690 :
1691 170 : m_pFeatureDefn->SetGeomType(ogrGeoType);
1692 :
1693 170 : if (wkbFlatten(ogrGeoType) == wkbMultiLineString ||
1694 : wkbFlatten(ogrGeoType) == wkbMultiPoint)
1695 60 : m_forceMulti = true;
1696 :
1697 170 : if (wkid.length() > 0)
1698 : {
1699 166 : m_pSRS = new OGRSpatialReference();
1700 332 : if (m_pSRS->importFromEPSG(atoi(wkid.c_str())) != OGRERR_NONE)
1701 : {
1702 0 : delete m_pSRS;
1703 0 : m_pSRS = NULL;
1704 : }
1705 : else
1706 166 : return true;
1707 : }
1708 :
1709 4 : if (wkt.length() > 0)
1710 : {
1711 0 : if (!GDBToOGRSpatialReference(wkt, &m_pSRS))
1712 : {
1713 : //report error, but be passive about it
1714 : CPLError( CE_Warning, CPLE_AppDefined,
1715 0 : "Failed Mapping ESRI Spatial Reference");
1716 : }
1717 : }
1718 : else
1719 : {
1720 : //report error, but be passive about it
1721 4 : CPLError( CE_Warning, CPLE_AppDefined, "Empty Spatial Reference");
1722 : }
1723 :
1724 4 : return true;
1725 : }
1726 :
1727 : /************************************************************************/
1728 : /* ParseSpatialReference() */
1729 : /************************************************************************/
1730 :
1731 170 : bool FGdbLayer::ParseSpatialReference(CPLXMLNode* psSpatialRefNode,
1732 : string* pOutWkt, string* pOutWKID)
1733 : {
1734 170 : *pOutWkt = "";
1735 170 : *pOutWKID = "";
1736 :
1737 : CPLXMLNode* psSRItemNode;
1738 :
1739 : /* Loop through all the SRS elements we want to store */
1740 2032 : for( psSRItemNode = psSpatialRefNode->psChild;
1741 : psSRItemNode != NULL;
1742 : psSRItemNode = psSRItemNode->psNext )
1743 : {
1744 : /* The WKID maps (mostly) to an EPSG code */
1745 2028 : if( psSRItemNode->eType == CXT_Element &&
1746 : psSRItemNode->psChild != NULL &&
1747 : EQUAL(psSRItemNode->pszValue,"WKID") )
1748 : {
1749 166 : char* pszUnescaped = CPLUnescapeString(psSRItemNode->psChild->pszValue, NULL, CPLES_XML);
1750 166 : *pOutWKID = pszUnescaped;
1751 166 : CPLFree(pszUnescaped);
1752 : }
1753 : /* The WKT well-known text can be converted by OGR */
1754 1696 : else if( psSRItemNode->eType == CXT_Element &&
1755 : psSRItemNode->psChild != NULL &&
1756 : EQUAL(psSRItemNode->pszValue,"WKT") )
1757 : {
1758 166 : char* pszUnescaped = CPLUnescapeString(psSRItemNode->psChild->pszValue, NULL, CPLES_XML);
1759 166 : *pOutWkt = pszUnescaped;
1760 166 : CPLFree(pszUnescaped);
1761 : }
1762 :
1763 : }
1764 170 : return (*pOutWkt != "" || *pOutWKID != "");
1765 : }
1766 :
1767 : /************************************************************************/
1768 : /* GDBToOGRFields() */
1769 : /************************************************************************/
1770 :
1771 182 : bool FGdbLayer::GDBToOGRFields(CPLXMLNode* psRoot)
1772 : {
1773 182 : m_vOGRFieldToESRIField.clear();
1774 :
1775 182 : if (psRoot->psChild == NULL || psRoot->psChild->psNext == NULL)
1776 : {
1777 0 : CPLError( CE_Failure, CPLE_AppDefined, "Unrecognized GDB XML Schema");
1778 :
1779 0 : return false;
1780 : }
1781 :
1782 182 : psRoot = psRoot->psChild->psNext; //change root to "FieldArray"
1783 :
1784 : //CPLAssert(ogrToESRIFieldMapping.size() == pOGRFeatureDef->GetFieldCount());
1785 :
1786 : CPLXMLNode* psFieldNode;
1787 :
1788 1284 : for( psFieldNode = psRoot->psChild;
1789 : psFieldNode != NULL;
1790 : psFieldNode = psFieldNode->psNext )
1791 : {
1792 : //loop through all "Field" elements
1793 : //
1794 :
1795 1102 : if( psFieldNode->eType == CXT_Element && psFieldNode->psChild != NULL &&
1796 : EQUAL(psFieldNode->pszValue,"Field"))
1797 : {
1798 :
1799 : CPLXMLNode* psFieldItemNode;
1800 920 : std::string fieldName;
1801 920 : std::string fieldType;
1802 920 : int nLength = 0;
1803 920 : int nPrecision = 0;
1804 :
1805 : // loop through all items in Field element
1806 : //
1807 :
1808 9908 : for( psFieldItemNode = psFieldNode->psChild;
1809 : psFieldItemNode != NULL;
1810 : psFieldItemNode = psFieldItemNode->psNext )
1811 : {
1812 8988 : if (psFieldItemNode->eType == CXT_Element)
1813 : {
1814 :
1815 8068 : if (EQUAL(psFieldItemNode->pszValue,"Name"))
1816 : {
1817 : char* pszUnescaped = CPLUnescapeString(
1818 920 : psFieldItemNode->psChild->pszValue, NULL, CPLES_XML);
1819 920 : fieldName = pszUnescaped;
1820 920 : CPLFree(pszUnescaped);
1821 : }
1822 7148 : else if (EQUAL(psFieldItemNode->pszValue,"Type") )
1823 : {
1824 : char* pszUnescaped = CPLUnescapeString(
1825 920 : psFieldItemNode->psChild->pszValue, NULL, CPLES_XML);
1826 920 : fieldType = pszUnescaped;
1827 920 : CPLFree(pszUnescaped);
1828 : }
1829 6228 : else if (EQUAL(psFieldItemNode->pszValue,"GeometryDef") )
1830 : {
1831 170 : if (!ParseGeometryDef(psFieldItemNode))
1832 0 : return false; // if we failed parsing the GeometryDef, we are done!
1833 : }
1834 6058 : else if (EQUAL(psFieldItemNode->pszValue,"Length") )
1835 : {
1836 920 : nLength = atoi(psFieldItemNode->psChild->pszValue);
1837 : }
1838 5138 : else if (EQUAL(psFieldItemNode->pszValue,"Precision") )
1839 : {
1840 920 : nPrecision = atoi(psFieldItemNode->psChild->pszValue);
1841 : }
1842 : }
1843 : }
1844 :
1845 :
1846 : ///////////////////////////////////////////////////////////////////
1847 : // At this point we have parsed everything about the current field
1848 :
1849 :
1850 920 : if (fieldType == "esriFieldTypeGeometry")
1851 : {
1852 170 : m_strShapeFieldName = fieldName;
1853 :
1854 170 : continue; // finish here for special field - don't add as OGR fielddef
1855 : }
1856 750 : else if (fieldType == "esriFieldTypeOID")
1857 : {
1858 : //m_strOIDFieldName = fieldName; // already set by this point
1859 :
1860 182 : continue; // finish here for special field - don't add as OGR fielddef
1861 : }
1862 :
1863 : OGRFieldType ogrType;
1864 : //CPLDebug("FGDB", "name = %s, type = %s", fieldName.c_str(), fieldType.c_str() );
1865 568 : if (!GDBToOGRFieldType(fieldType, &ogrType))
1866 : {
1867 : // field cannot be mapped, skipping further processing
1868 : CPLError( CE_Warning, CPLE_AppDefined, "Skipping field: [%s] type: [%s] ",
1869 0 : fieldName.c_str(), fieldType.c_str() );
1870 0 : continue;
1871 : }
1872 :
1873 :
1874 : //TODO: Optimization - modify m_wstrSubFields so it only fetches fields that are mapped
1875 :
1876 568 : OGRFieldDefn fieldTemplate( fieldName.c_str(), ogrType);
1877 : //fieldTemplate.SetWidth(nLength);
1878 : //fieldTemplate.SetPrecision(nPrecision);
1879 568 : m_pFeatureDefn->AddFieldDefn( &fieldTemplate );
1880 :
1881 568 : m_vOGRFieldToESRIField.push_back(StringToWString(fieldName));
1882 568 : m_vOGRFieldToESRIFieldType.push_back( fieldType );
1883 :
1884 : }
1885 : }
1886 :
1887 182 : return true;
1888 : }
1889 :
1890 :
1891 : /************************************************************************/
1892 : /* ResetReading() */
1893 : /************************************************************************/
1894 :
1895 419 : void FGdbLayer::ResetReading()
1896 : {
1897 : long hr;
1898 :
1899 419 : EndBulkLoad();
1900 :
1901 419 : if (m_pOGRFilterGeometry && !m_pOGRFilterGeometry->IsEmpty())
1902 : {
1903 : // Search spatial
1904 : // As of beta1, FileGDB only supports bbox searched, if we have GEOS installed,
1905 : // we can do the rest ourselves.
1906 :
1907 78 : OGREnvelope ogrEnv;
1908 :
1909 78 : m_pOGRFilterGeometry->getEnvelope(&ogrEnv);
1910 :
1911 : //spatial query
1912 78 : FileGDBAPI::Envelope env(ogrEnv.MinX, ogrEnv.MaxX, ogrEnv.MinY, ogrEnv.MaxY);
1913 :
1914 78 : if FAILED(hr = m_pTable->Search(m_wstrSubfields, m_wstrWhereClause, env, true, *m_pEnumRows))
1915 0 : GDBErr(hr, "Failed Searching");
1916 :
1917 78 : m_bFilterDirty = false;
1918 :
1919 78 : return;
1920 : }
1921 :
1922 : // Search non-spatial
1923 :
1924 341 : if FAILED(hr = m_pTable->Search(m_wstrSubfields, m_wstrWhereClause, true, *m_pEnumRows))
1925 0 : GDBErr(hr, "Failed Searching");
1926 :
1927 341 : m_bFilterDirty = false;
1928 :
1929 : }
1930 :
1931 : /************************************************************************/
1932 : /* SetSpatialFilter() */
1933 : /************************************************************************/
1934 :
1935 183 : void FGdbLayer::SetSpatialFilter( OGRGeometry* pOGRGeom )
1936 : {
1937 183 : if (m_pOGRFilterGeometry)
1938 : {
1939 26 : OGRGeometryFactory::destroyGeometry(m_pOGRFilterGeometry);
1940 26 : m_pOGRFilterGeometry = NULL;
1941 : }
1942 :
1943 183 : if (pOGRGeom == NULL || pOGRGeom->IsEmpty())
1944 : {
1945 157 : m_bFilterDirty = true;
1946 :
1947 157 : return;
1948 : }
1949 :
1950 26 : m_pOGRFilterGeometry = pOGRGeom->clone();
1951 :
1952 26 : m_pOGRFilterGeometry->transformTo(m_pSRS);
1953 :
1954 26 : m_bFilterDirty = true;
1955 : }
1956 :
1957 : /************************************************************************/
1958 : /* SetSpatialFilterRect() */
1959 : /************************************************************************/
1960 :
1961 0 : void FGdbLayer::SetSpatialFilterRect (double dfMinX, double dfMinY, double dfMaxX, double dfMaxY)
1962 : {
1963 :
1964 : //TODO: can optimize this by changing how the filter gets generated -
1965 : //this will work for now
1966 :
1967 0 : OGRGeometry* pTemp = OGRGeometryFactory::createGeometry(wkbPolygon);
1968 :
1969 0 : pTemp->assignSpatialReference(m_pSRS);
1970 :
1971 0 : OGRLinearRing ring;
1972 :
1973 0 : ring.addPoint( dfMinX, dfMinY );
1974 0 : ring.addPoint( dfMinX, dfMaxY );
1975 0 : ring.addPoint( dfMaxX, dfMaxY );
1976 0 : ring.addPoint( dfMaxX, dfMinY );
1977 0 : ring.addPoint( dfMinX, dfMinY );
1978 0 : ((OGRPolygon *) pTemp)->addRing( &ring );
1979 :
1980 0 : SetSpatialFilter(pTemp);
1981 :
1982 0 : OGRGeometryFactory::destroyGeometry(pTemp);
1983 0 : }
1984 :
1985 :
1986 : /************************************************************************/
1987 : /* SetAttributeFilter() */
1988 : /************************************************************************/
1989 :
1990 144 : OGRErr FGdbLayer::SetAttributeFilter( const char* pszQuery )
1991 : {
1992 144 : m_wstrWhereClause = StringToWString( (pszQuery != NULL) ? pszQuery : "" );
1993 :
1994 144 : m_bFilterDirty = true;
1995 :
1996 144 : return OGRERR_NONE;
1997 : }
1998 :
1999 : /************************************************************************/
2000 : /* OGRFeatureFromGdbRow() */
2001 : /************************************************************************/
2002 :
2003 959 : bool FGdbBaseLayer::OGRFeatureFromGdbRow(Row* pRow, OGRFeature** ppFeature)
2004 : {
2005 : long hr;
2006 :
2007 959 : OGRFeature* pOutFeature = new OGRFeature(m_pFeatureDefn);
2008 :
2009 : /////////////////////////////////////////////////////////
2010 : // Translate OID
2011 : //
2012 :
2013 959 : int32 oid = -1;
2014 959 : if (FAILED(hr = pRow->GetOID(oid)))
2015 : {
2016 : //this should never happen
2017 0 : delete pOutFeature;
2018 0 : return false;
2019 : }
2020 959 : pOutFeature->SetFID(oid);
2021 :
2022 :
2023 : /////////////////////////////////////////////////////////
2024 : // Translate Geometry
2025 : //
2026 :
2027 959 : ShapeBuffer gdbGeometry;
2028 : // Row::GetGeometry() will fail with -2147467259 for NULL geometries
2029 : // Row::GetGeometry() will fail with -2147219885 for tables without a geometry field
2030 959 : if (!FAILED(hr = pRow->GetGeometry(gdbGeometry)))
2031 : {
2032 801 : OGRGeometry* pOGRGeo = NULL;
2033 :
2034 801 : if ((!GDBGeometryToOGRGeometry(m_forceMulti, &gdbGeometry, m_pSRS, &pOGRGeo)) || pOGRGeo == NULL)
2035 : {
2036 0 : delete pOutFeature;
2037 0 : return GDBErr(hr, "Failed to translate FileGDB Geometry to OGR Geometry for row " + string(CPLSPrintf("%d", (int)oid)));
2038 : }
2039 :
2040 801 : pOutFeature->SetGeometryDirectly(pOGRGeo);
2041 : }
2042 :
2043 :
2044 : //////////////////////////////////////////////////////////
2045 : // Map fields
2046 : //
2047 :
2048 :
2049 959 : size_t mappedFieldCount = m_vOGRFieldToESRIField.size();
2050 :
2051 959 : bool foundBadColumn = false;
2052 :
2053 4697 : for (size_t i = 0; i < mappedFieldCount; ++i)
2054 : {
2055 3738 : const wstring & wstrFieldName = m_vOGRFieldToESRIField[i];
2056 3738 : const std::string & strFieldType = m_vOGRFieldToESRIFieldType[i];
2057 :
2058 3738 : bool isNull = false;
2059 :
2060 3738 : if (FAILED(hr = pRow->IsNull(wstrFieldName, isNull)))
2061 : {
2062 : GDBErr(hr, "Failed to determine NULL status from column " +
2063 0 : WStringToString(wstrFieldName));
2064 0 : foundBadColumn = true;
2065 0 : continue;
2066 : }
2067 :
2068 3738 : if (isNull)
2069 : {
2070 40 : continue; //leave as unset
2071 : }
2072 :
2073 : //
2074 : // NOTE: This switch statement needs to be kept in sync with GDBToOGRFieldType utility function
2075 : // since we are only checking for types we mapped in that utility function
2076 :
2077 3698 : switch (m_pFeatureDefn->GetFieldDefn(i)->GetType())
2078 : {
2079 :
2080 : case OFTInteger:
2081 : {
2082 : int32 val;
2083 :
2084 1717 : if (FAILED(hr = pRow->GetInteger(wstrFieldName, val)))
2085 : {
2086 : int16 shortval;
2087 1 : if (FAILED(hr = pRow->GetShort(wstrFieldName, shortval)))
2088 : {
2089 : GDBErr(hr, "Failed to determine integer value for column " +
2090 0 : WStringToString(wstrFieldName));
2091 0 : foundBadColumn = true;
2092 0 : continue;
2093 : }
2094 1 : val = shortval;
2095 : }
2096 :
2097 1717 : pOutFeature->SetField(i, (int)val);
2098 : }
2099 1717 : break;
2100 :
2101 : case OFTReal:
2102 : {
2103 1059 : if (strFieldType == "esriFieldTypeSingle")
2104 : {
2105 : float val;
2106 :
2107 1 : if (FAILED(hr = pRow->GetFloat(wstrFieldName, val)))
2108 : {
2109 : GDBErr(hr, "Failed to determine float value for column " +
2110 0 : WStringToString(wstrFieldName));
2111 0 : foundBadColumn = true;
2112 0 : continue;
2113 : }
2114 :
2115 1 : pOutFeature->SetField(i, val);
2116 : }
2117 : else
2118 : {
2119 : double val;
2120 :
2121 1058 : if (FAILED(hr = pRow->GetDouble(wstrFieldName, val)))
2122 : {
2123 : GDBErr(hr, "Failed to determine real value for column " +
2124 0 : WStringToString(wstrFieldName));
2125 0 : foundBadColumn = true;
2126 0 : continue;
2127 : }
2128 :
2129 1058 : pOutFeature->SetField(i, val);
2130 : }
2131 : }
2132 1059 : break;
2133 : case OFTString:
2134 : {
2135 921 : wstring val;
2136 :
2137 921 : if( strFieldType == "esriFieldTypeGlobalID" )
2138 : {
2139 1 : Guid guid;
2140 1 : if( FAILED(hr = pRow->GetGlobalID(guid)) ||
2141 : FAILED(hr = guid.ToString(val)) )
2142 : {
2143 : GDBErr(hr, "Failed to determine string value for column " +
2144 0 : WStringToString(wstrFieldName));
2145 0 : foundBadColumn = true;
2146 0 : continue;
2147 0 : }
2148 : }
2149 920 : else if( strFieldType == "esriFieldTypeGUID" )
2150 : {
2151 1 : Guid guid;
2152 1 : if( FAILED(hr = pRow->GetGUID(wstrFieldName, guid)) ||
2153 : FAILED(hr = guid.ToString(val)) )
2154 : {
2155 : GDBErr(hr, "Failed to determine string value for column " +
2156 0 : WStringToString(wstrFieldName));
2157 0 : foundBadColumn = true;
2158 0 : continue;
2159 0 : }
2160 : }
2161 : else
2162 : {
2163 919 : if (FAILED(hr = pRow->GetString(wstrFieldName, val)))
2164 : {
2165 : GDBErr(hr, "Failed to determine string value for column " +
2166 0 : WStringToString(wstrFieldName));
2167 0 : foundBadColumn = true;
2168 0 : continue;
2169 : }
2170 : }
2171 :
2172 921 : pOutFeature->SetField(i, WStringToString(val).c_str());
2173 : }
2174 921 : break;
2175 :
2176 : /* TODO: Need to get test dataset to implement these leave it as NULL for now
2177 : case OFTBinary:
2178 : {
2179 : ByteArray binaryBuf;
2180 :
2181 : if (FAILED(hr = pRow->GetBinary(wstrFieldName, binaryBuf)))
2182 : {
2183 : GDBErr(hr, "Failed to determine binary value for column " + WStringToString(wstrFieldName));
2184 : foundBadColumn = true;
2185 : continue;
2186 : }
2187 :
2188 : pOutFeature->SetField(i, (int)binaryBuf.inUseLength, (GByte*)binaryBuf.byteArray);
2189 : }
2190 : break;
2191 : */
2192 :
2193 : case OFTDateTime:
2194 : {
2195 : struct tm val;
2196 :
2197 1 : if (FAILED(hr = pRow->GetDate(wstrFieldName, val)))
2198 : {
2199 : GDBErr(hr, "Failed to determine date value for column " +
2200 0 : WStringToString(wstrFieldName));
2201 0 : foundBadColumn = true;
2202 0 : continue;
2203 : }
2204 :
2205 : pOutFeature->SetField(i, val.tm_year + 1900, val.tm_mon + 1,
2206 1 : val.tm_mday, val.tm_hour, val.tm_min, val.tm_sec);
2207 : // Examine test data to figure out how to extract that
2208 : }
2209 1 : break;
2210 :
2211 : default:
2212 : {
2213 0 : if (!m_supressColumnMappingError)
2214 : {
2215 0 : foundBadColumn = true;
2216 : CPLError( CE_Warning, CPLE_AppDefined,
2217 : "Row id: %d col:%d has unhandled col type (%d). Setting to NULL.",
2218 0 : (int)oid, (int)i, m_pFeatureDefn->GetFieldDefn(i)->GetType());
2219 : }
2220 : }
2221 : }
2222 : }
2223 :
2224 959 : if (foundBadColumn)
2225 0 : m_supressColumnMappingError = true;
2226 :
2227 :
2228 959 : *ppFeature = pOutFeature;
2229 :
2230 959 : return true;
2231 : }
2232 :
2233 :
2234 : /************************************************************************/
2235 : /* GetNextFeature() */
2236 : /************************************************************************/
2237 :
2238 1060 : OGRFeature* FGdbLayer::GetNextFeature()
2239 : {
2240 1060 : if (m_bFilterDirty)
2241 21 : ResetReading();
2242 :
2243 1060 : EndBulkLoad();
2244 :
2245 1060 : return FGdbBaseLayer::GetNextFeature();
2246 : }
2247 :
2248 : /************************************************************************/
2249 : /* GetFeature() */
2250 : /************************************************************************/
2251 :
2252 112 : OGRFeature *FGdbLayer::GetFeature( long oid )
2253 : {
2254 : // do query to fetch individual row
2255 112 : EnumRows enumRows;
2256 112 : Row row;
2257 :
2258 112 : EndBulkLoad();
2259 :
2260 112 : if (GetRow(enumRows, row, oid) != OGRERR_NONE)
2261 48 : return NULL;
2262 :
2263 64 : OGRFeature* pOGRFeature = NULL;
2264 :
2265 64 : if (!OGRFeatureFromGdbRow(&row, &pOGRFeature))
2266 : {
2267 0 : return NULL;
2268 : }
2269 :
2270 64 : return pOGRFeature;
2271 : }
2272 :
2273 :
2274 : /************************************************************************/
2275 : /* GetFeatureCount() */
2276 : /************************************************************************/
2277 :
2278 204 : int FGdbLayer::GetFeatureCount( int bForce )
2279 : {
2280 : long hr;
2281 204 : int32 rowCount = 0;
2282 :
2283 204 : EndBulkLoad();
2284 :
2285 204 : if (m_pOGRFilterGeometry != NULL || m_wstrWhereClause.size() != 0)
2286 58 : return OGRLayer::GetFeatureCount(bForce);
2287 :
2288 146 : if (FAILED(hr = m_pTable->GetRowCount(rowCount)))
2289 : {
2290 0 : GDBErr(hr, "Failed counting rows");
2291 0 : return 0;
2292 : }
2293 :
2294 : #if 0
2295 : Row row;
2296 : EnumRows enumRows;
2297 :
2298 : if (FAILED(hr = m_pTable->Search(StringToWString(m_strOIDFieldName), L"", true, enumRows)))
2299 : {
2300 : GDBErr(hr, "Failed counting rows");
2301 : return -1;
2302 : }
2303 :
2304 : while (S_OK == (hr = enumRows.Next(row)))
2305 : ++rowCount;
2306 :
2307 : if (FAILED(hr))
2308 : {
2309 : GDBErr(hr, "Failed counting rows (during fetch)");
2310 : return -1;
2311 : }
2312 : #endif
2313 :
2314 146 : return static_cast<int>(rowCount);
2315 : }
2316 :
2317 :
2318 :
2319 : /************************************************************************/
2320 : /* GetExtent() */
2321 : /************************************************************************/
2322 :
2323 30 : OGRErr FGdbLayer::GetExtent (OGREnvelope* psExtent, int bForce)
2324 : {
2325 30 : if (m_pOGRFilterGeometry != NULL || m_wstrWhereClause.size() != 0 ||
2326 : m_strShapeFieldName.size() == 0)
2327 1 : return OGRLayer::GetExtent(psExtent, bForce);
2328 :
2329 : long hr;
2330 29 : Envelope envelope;
2331 29 : if (FAILED(hr = m_pTable->GetExtent(envelope)))
2332 : {
2333 0 : GDBErr(hr, "Failed fetching extent");
2334 0 : return OGRERR_FAILURE;
2335 : }
2336 :
2337 29 : psExtent->MinX = envelope.xMin;
2338 29 : psExtent->MinY = envelope.yMin;
2339 29 : psExtent->MaxX = envelope.xMax;
2340 29 : psExtent->MaxY = envelope.yMax;
2341 :
2342 29 : if (CPLIsNan(psExtent->MinX) ||
2343 : CPLIsNan(psExtent->MinY) ||
2344 : CPLIsNan(psExtent->MaxX) ||
2345 : CPLIsNan(psExtent->MaxY))
2346 2 : return OGRERR_FAILURE;
2347 :
2348 27 : return OGRERR_NONE;
2349 : }
2350 :
2351 : /************************************************************************/
2352 : /* StartBulkLoad() */
2353 : /************************************************************************/
2354 :
2355 1 : void FGdbLayer::StartBulkLoad ()
2356 : {
2357 1 : if ( ! m_pTable )
2358 0 : return;
2359 :
2360 1 : if ( m_bBulkLoadInProgress )
2361 0 : return;
2362 :
2363 1 : m_bBulkLoadInProgress = TRUE;
2364 1 : m_pTable->LoadOnlyMode(true);
2365 1 : m_pTable->SetWriteLock();
2366 : }
2367 :
2368 : /************************************************************************/
2369 : /* EndBulkLoad() */
2370 : /************************************************************************/
2371 :
2372 2061 : void FGdbLayer::EndBulkLoad ()
2373 : {
2374 2061 : if ( ! m_pTable )
2375 3 : return;
2376 :
2377 2058 : if ( ! m_bBulkLoadInProgress )
2378 2057 : return;
2379 :
2380 1 : m_bBulkLoadInProgress = FALSE;
2381 1 : m_bBulkLoadAllowed = -1; /* so that the configuration option is read the first time we CreateFeature() again */
2382 1 : m_pTable->LoadOnlyMode(false);
2383 1 : m_pTable->FreeWriteLock();
2384 : }
2385 :
2386 : /* OGRErr FGdbLayer::StartTransaction ()
2387 : {
2388 : if ( ! m_pTable )
2389 : return OGRERR_FAILURE;
2390 :
2391 : m_pTable->LoadOnlyMode(true);
2392 : m_pTable->SetWriteLock();
2393 : return OGRERR_NONE;
2394 :
2395 : } */
2396 :
2397 :
2398 : /* OGRErr FGdbLayer::CommitTransaction ()
2399 : {
2400 : if ( ! m_pTable )
2401 : return OGRERR_FAILURE;
2402 :
2403 : m_pTable->LoadOnlyMode(false);
2404 : m_pTable->FreeWriteLock();
2405 : return OGRERR_NONE;
2406 : } */
2407 :
2408 : /* OGRErr FGdbLayer::RollbackTransaction ()
2409 : {
2410 : if ( ! m_pTable )
2411 : return OGRERR_FAILURE;
2412 :
2413 : m_pTable->LoadOnlyMode(false);
2414 : m_pTable->FreeWriteLock();
2415 : return OGRERR_NONE;
2416 : } */
2417 :
2418 :
2419 : /************************************************************************/
2420 : /* GetLayerXML() */
2421 : /* Return XML definition of the Layer as provided by FGDB. Caller must */
2422 : /* free result. */
2423 : /* Not currently used by the driver, but can be used by external code */
2424 : /* for specific purposes. */
2425 : /************************************************************************/
2426 :
2427 15 : OGRErr FGdbLayer::GetLayerXML (char **ppXml)
2428 : {
2429 : long hr;
2430 15 : std::string xml;
2431 :
2432 15 : if ( FAILED(hr = m_pTable->GetDefinition(xml)) )
2433 : {
2434 0 : GDBErr(hr, "Failed fetching XML table definition");
2435 0 : return OGRERR_FAILURE;
2436 : }
2437 :
2438 15 : *ppXml = CPLStrdup(xml.c_str());
2439 15 : return OGRERR_NONE;
2440 : }
2441 :
2442 : /************************************************************************/
2443 : /* GetLayerMetadataXML() */
2444 : /* Return XML metadata for the Layer as provided by FGDB. Caller must */
2445 : /* free result. */
2446 : /* Not currently used by the driver, but can be used by external code */
2447 : /* for specific purposes. */
2448 : /************************************************************************/
2449 :
2450 15 : OGRErr FGdbLayer::GetLayerMetadataXML (char **ppXml)
2451 : {
2452 : long hr;
2453 15 : std::string xml;
2454 :
2455 15 : if ( FAILED(hr = m_pTable->GetDocumentation(xml)) )
2456 : {
2457 0 : GDBErr(hr, "Failed fetching XML table metadata");
2458 0 : return OGRERR_FAILURE;
2459 : }
2460 :
2461 15 : *ppXml = CPLStrdup(xml.c_str());
2462 15 : return OGRERR_NONE;
2463 : }
2464 :
2465 : /************************************************************************/
2466 : /* TestCapability() */
2467 : /************************************************************************/
2468 :
2469 192 : int FGdbLayer::TestCapability( const char* pszCap )
2470 : {
2471 :
2472 192 : if (EQUAL(pszCap,OLCRandomRead))
2473 48 : return TRUE;
2474 :
2475 144 : else if (EQUAL(pszCap,OLCFastFeatureCount))
2476 0 : return m_pOGRFilterGeometry == NULL && m_wstrWhereClause.size() == 0;
2477 :
2478 144 : else if (EQUAL(pszCap,OLCFastSpatialFilter))
2479 0 : return TRUE;
2480 :
2481 144 : else if (EQUAL(pszCap,OLCFastGetExtent))
2482 0 : return m_pOGRFilterGeometry == NULL && m_wstrWhereClause.size() == 0;
2483 :
2484 144 : else if (EQUAL(pszCap,OLCCreateField)) /* CreateField() */
2485 0 : return TRUE;
2486 :
2487 144 : else if (EQUAL(pszCap,OLCSequentialWrite)) /* CreateFeature() */
2488 16 : return TRUE;
2489 :
2490 128 : else if (EQUAL(pszCap,OLCStringsAsUTF8)) /* Native UTF16, converted to UTF8 */
2491 16 : return TRUE;
2492 :
2493 112 : else if (EQUAL(pszCap,OLCReorderFields)) /* TBD ReorderFields() */
2494 0 : return FALSE;
2495 :
2496 112 : else if (EQUAL(pszCap,OLCDeleteFeature)) /* DeleteFeature() */
2497 16 : return TRUE;
2498 :
2499 96 : else if (EQUAL(pszCap,OLCRandomWrite)) /* SetFeature() */
2500 16 : return TRUE;
2501 :
2502 80 : else if (EQUAL(pszCap,OLCDeleteField)) /* DeleteField() */
2503 0 : return TRUE;
2504 :
2505 : #ifdef AlterFieldDefn_implemented_but_not_working
2506 : else if (EQUAL(pszCap,OLCAlterFieldDefn)) /* AlterFieldDefn() */
2507 : return TRUE;
2508 : #endif
2509 :
2510 80 : else if (EQUAL(pszCap,OLCFastSetNextByIndex)) /* TBD FastSetNextByIndex() */
2511 16 : return FALSE;
2512 :
2513 64 : else if (EQUAL(pszCap,OLCTransactions)) /* TBD Start/End Transactions() */
2514 32 : return FALSE;
2515 :
2516 : else
2517 32 : return FALSE;
2518 2139 : }
|