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