1 : /******************************************************************************
2 : * $Id: FGdbLayer.cpp 23394 2011-11-19 19:20:12Z 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 23394 2011-11-19 19:20:12Z rouault $");
40 :
41 : using std::string;
42 : using std::wstring;
43 :
44 : /************************************************************************/
45 : /* FGdbLayer() */
46 : /************************************************************************/
47 81 : 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 81 : m_supressColumnMappingError(false), m_forceMulti(false)
52 : {
53 81 : m_pEnumRows = new EnumRows;
54 81 : }
55 :
56 : /************************************************************************/
57 : /* ~FGdbLayer() */
58 : /************************************************************************/
59 :
60 81 : FGdbLayer::~FGdbLayer()
61 : {
62 81 : if (m_pFeatureDefn)
63 : {
64 81 : m_pFeatureDefn->Release();
65 81 : m_pFeatureDefn = NULL;
66 : }
67 :
68 81 : if (m_pSRS)
69 : {
70 79 : m_pSRS->Release();
71 79 : m_pSRS = NULL;
72 : }
73 :
74 81 : if (m_pEnumRows)
75 : {
76 81 : delete m_pEnumRows;
77 81 : m_pEnumRows = NULL;
78 : }
79 :
80 : // NOTE: never delete m_pDS - the memory doesn't belong to us
81 : // TODO: check if we need to close the table or if the destructor
82 : // takes care of closing as it should
83 81 : if (m_pTable)
84 : {
85 81 : delete m_pTable;
86 81 : m_pTable = NULL;
87 : }
88 :
89 81 : if (m_pOGRFilterGeometry)
90 : {
91 0 : OGRGeometryFactory::destroyGeometry(m_pOGRFilterGeometry);
92 0 : m_pOGRFilterGeometry = NULL;
93 : }
94 :
95 81 : }
96 :
97 :
98 : /************************************************************************/
99 : /* CreateFeature() */
100 : /* Create an FGDB Row and populate it from an OGRFeature. */
101 : /* */
102 : /************************************************************************/
103 :
104 22 : OGRErr FGdbLayer::CreateFeature( OGRFeature *poFeature )
105 : {
106 22 : Table *fgdb_table = m_pTable;
107 22 : Row fgdb_row;
108 : fgdbError hr;
109 22 : ShapeBuffer shape;
110 :
111 22 : hr = fgdb_table->CreateRowObject(fgdb_row);
112 :
113 : /* Check the status of the Row create */
114 22 : if (FAILED(hr))
115 0 : return GDBErr(hr, "Failed at creating Row in CreateFeature.");
116 :
117 22 : OGRFeatureDefn* poFeatureDefn = m_pFeatureDefn;
118 22 : int nFieldCount = poFeatureDefn->GetFieldCount();
119 :
120 : /* Copy the OGR visible fields (everything except geometry and FID) */
121 22 : for( int i = 0; i < nFieldCount; i++ )
122 : {
123 66 : std::string field_name = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
124 66 : std::wstring wfield_name = StringToWString(field_name);
125 :
126 : /* Set empty fields to NULL */
127 66 : if( !poFeature->IsFieldSet( i ) )
128 : {
129 0 : if (FAILED(hr = fgdb_row.SetNull(wfield_name)))
130 0 : return GDBErr(hr, "Failed setting field to NULL.");
131 0 : continue;
132 : }
133 :
134 : /* Set the information using the appropriate FGDB function */
135 66 : int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
136 :
137 66 : if ( nOGRFieldType == OFTInteger )
138 : {
139 : /* Integers (we don't do FGDB Shorts) */
140 12 : int fldvalue = poFeature->GetFieldAsInteger(i);
141 12 : hr = fgdb_row.SetInteger(wfield_name, fldvalue);
142 : }
143 54 : else if ( nOGRFieldType == OFTReal )
144 : {
145 : /* Doubles (we don't handle FGDB Floats) */
146 32 : double fldvalue = poFeature->GetFieldAsDouble(i);
147 32 : hr = fgdb_row.SetDouble(wfield_name, fldvalue);
148 : }
149 22 : else if ( nOGRFieldType == OFTString )
150 : {
151 : /* Strings we convert to wstring */
152 22 : std::string fldvalue = poFeature->GetFieldAsString(i);
153 22 : std::wstring wfldvalue = StringToWString(fldvalue);
154 22 : hr = fgdb_row.SetString(wfield_name, wfldvalue);
155 : }
156 0 : else if ( nOGRFieldType == OFTDateTime || nOGRFieldType == OFTDate )
157 : {
158 : /* Dates we need to coerce a little */
159 : struct tm val;
160 : poFeature->GetFieldAsDateTime(i, &(val.tm_year), &(val.tm_mon), &(val.tm_mday),
161 0 : &(val.tm_hour), &(val.tm_min), &(val.tm_sec), NULL);
162 0 : val.tm_year -= 1900;
163 0 : val.tm_mon = val.tm_mon - 1; /* OGR months go 1-12, FGDB go 0-11 */
164 0 : hr = fgdb_row.SetDate(wfield_name, val);
165 : }
166 0 : else if ( nOGRFieldType == OFTBinary )
167 : {
168 : /* Binary data */
169 0 : ByteArray fgdb_bytearray;
170 : int bytesize;
171 0 : GByte *bytes = poFeature->GetFieldAsBinary(i, &bytesize);
172 0 : if ( bytesize )
173 : {
174 0 : fgdb_bytearray.Allocate(bytesize);
175 0 : memcpy(fgdb_bytearray.byteArray, bytes, bytesize);
176 0 : fgdb_bytearray.inUseLength = bytesize;
177 0 : hr = fgdb_row.SetBinary(wfield_name, fgdb_bytearray);
178 : }
179 : else
180 : {
181 0 : hr = fgdb_row.SetNull(wfield_name);
182 0 : }
183 : }
184 : else
185 : {
186 : /* We can't handle this type */
187 : CPLError( CE_Failure, CPLE_AppDefined,
188 0 : "FGDB driver does not support OGR type." );
189 0 : return OGRERR_FAILURE;
190 : }
191 : }
192 :
193 : /* Done with attribute fields, now do geometry */
194 22 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
195 :
196 : /* Write geometry to a buffer */
197 22 : GByte *pabyShape = NULL;
198 22 : int nShapeSize = 0;
199 22 : OGRErr err = OGRWriteToShapeBin( poGeom, &pabyShape, &nShapeSize );
200 22 : if ( err != OGRERR_NONE )
201 0 : return err;
202 :
203 : /* Copy it into a ShapeBuffer */
204 22 : if ( nShapeSize > 0 )
205 : {
206 22 : shape.Allocate(nShapeSize);
207 22 : memcpy(shape.shapeBuffer, pabyShape, nShapeSize);
208 22 : shape.inUseLength = nShapeSize;
209 : }
210 :
211 : /* Free the shape buffer */
212 22 : CPLFree(pabyShape);
213 :
214 : /* Write ShapeBuffer into the Row */
215 22 : hr = fgdb_row.SetGeometry(shape);
216 22 : if (FAILED(hr))
217 0 : return GDBErr(hr, "Failed at writing Geometry to Row in CreateFeature.");
218 :
219 : /* Cannot write to FID field - it is managed by GDB*/
220 : //std::wstring wfield_name = StringToWString(m_strOIDFieldName);
221 : //hr = fgdb_row.SetInteger(wfield_name, poFeature->GetFID());
222 :
223 : /* Write the row to the table */
224 22 : hr = fgdb_table->Insert(fgdb_row);
225 22 : if (FAILED(hr))
226 0 : return GDBErr(hr, "Failed at writing Row to Table in CreateFeature.");
227 :
228 22 : return OGRERR_NONE;
229 :
230 : }
231 :
232 : /************************************************************************/
233 : /* CreateField() */
234 : /* Build up an FGDB XML field definition and use it to create a Field */
235 : /* Update the OGRFeatureDefn to reflect the new field. */
236 : /* */
237 : /************************************************************************/
238 :
239 41 : OGRErr FGdbLayer::CreateField(OGRFieldDefn* poField, int bApproxOK)
240 : {
241 41 : OGRFieldDefn oField(poField);
242 41 : std::string fieldname = oField.GetNameRef();
243 41 : std::wstring wfieldname = StringToWString(fieldname);
244 41 : std::string fidname = std::string(GetFIDColumn());
245 82 : std::string nullable = "true";
246 41 : Table *fgdb_table = m_pTable;
247 :
248 : /* Try to map the OGR type to an ESRI type */
249 82 : OGRFieldType fldtype = oField.GetType();
250 41 : std::string gdbFieldType;
251 41 : if ( ! OGRToGDBFieldType(fldtype, &gdbFieldType) )
252 : {
253 0 : GDBErr(-1, "Failed converting field type.");
254 0 : return OGRERR_FAILURE;
255 : }
256 :
257 : /* If we don't have our FGDB Table pointer intialized, we can quit now. */
258 41 : if ( ! m_pTable )
259 : {
260 0 : GDBErr(-1, "FGDB Table has not been initialized.");
261 0 : return OGRERR_FAILURE;
262 : }
263 :
264 : /* Then the Field definition */
265 41 : CPLXMLNode *defn_xml = CPLCreateXMLNode(NULL, CXT_Element, "esri:Field");
266 :
267 : /* Add the XML attributes to the Field node */
268 41 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
269 41 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xs", "http://www.w3.org/2001/XMLSchema");
270 41 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:esri", "http://www.esri.com/schemas/ArcGIS/10.1");
271 41 : FGDB_CPLAddXMLAttribute(defn_xml, "xsi:type", "esri:Field");
272 :
273 : /* Basic field information */
274 41 : CPLCreateXMLElementAndValue(defn_xml, "Name", fieldname.c_str());
275 41 : CPLCreateXMLElementAndValue(defn_xml, "Type", gdbFieldType.c_str());
276 41 : CPLCreateXMLElementAndValue(defn_xml, "IsNullable", nullable.c_str());
277 :
278 : /* Get the Width and Precision if we know them */
279 41 : int width = oField.GetWidth();
280 41 : int precision = oField.GetPrecision();
281 41 : if ( width <= 0 )
282 38 : GDBFieldTypeToWidthPrecision(gdbFieldType, &width, &precision);
283 :
284 : /* Write out the Width and Precision */
285 : char buf[100];
286 41 : snprintf(buf, 100, "%d", width);
287 41 : CPLCreateXMLElementAndValue(defn_xml,"Length", buf);
288 41 : snprintf(buf, 100, "%d", precision);
289 41 : CPLCreateXMLElementAndValue(defn_xml,"Precision", buf);
290 :
291 : /* We know nothing about Scale, so zero it out */
292 41 : CPLCreateXMLElementAndValue(defn_xml,"Scale", "0");
293 :
294 : /* Default values are discouraged in OGR API docs */
295 : /* <DefaultValue xsi:type="xs:string">afternoon</DefaultValue> */
296 :
297 : /* Convert our XML tree into a string for FGDB */
298 41 : char *defn_str = CPLSerializeXMLTree(defn_xml);
299 41 : CPLDebug("FGDB", "CreateField() generated XML for FGDB\n%s", defn_str);
300 :
301 : /* Add the FGDB Field to the FGDB Table. */
302 41 : fgdbError hr = fgdb_table->AddField(defn_str);
303 :
304 : /* Free the XML */
305 41 : CPLFree(defn_str);
306 41 : CPLDestroyXMLNode(defn_xml);
307 :
308 : /* Check the status of the Field add */
309 41 : if (FAILED(hr))
310 0 : return GDBErr(hr, "Failed at creating Field for " + fieldname);
311 :
312 : /* Now add the OGRFieldDefn to the OGRFeatureDefn */
313 41 : m_pFeatureDefn->AddFieldDefn(&oField);
314 :
315 41 : m_vOGRFieldToESRIField.push_back(StringToWString(fieldname));
316 41 : m_vOGRFieldToESRIFieldType.push_back( gdbFieldType );
317 :
318 : /* All done and happy */
319 41 : return OGRERR_NONE;
320 :
321 : }
322 :
323 : /************************************************************************/
324 : /* XMLSpatialReference() */
325 : /* Build up an XML representation of an OGRSpatialReference. */
326 : /* Used in layer creation. */
327 : /* */
328 : /************************************************************************/
329 :
330 35 : CPLXMLNode* XMLSpatialReference(OGRSpatialReference* poSRS, char** papszOptions)
331 : {
332 : /* We always need a SpatialReference */
333 35 : CPLXMLNode *srs_xml = CPLCreateXMLNode(NULL, CXT_Element, "SpatialReference");
334 :
335 : /* Extract the WKID before morphing */
336 35 : char *wkid = NULL;
337 35 : if ( poSRS && poSRS->GetAuthorityCode(NULL) )
338 : {
339 33 : wkid = CPLStrdup(poSRS->GetAuthorityCode(NULL));
340 : }
341 :
342 : /* NULL poSRS => UnknownCoordinateSystem */
343 35 : if ( ! poSRS )
344 : {
345 2 : FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:UnknownCoordinateSystem");
346 : }
347 : else
348 : {
349 : /* Make a clone so we can morph it without morphing the original */
350 33 : OGRSpatialReference* poSRSClone = poSRS->Clone();
351 :
352 : /* Flip the WKT to ESRI form, return UnknownCoordinateSystem if we can't */
353 33 : if ( poSRSClone->morphToESRI() != OGRERR_NONE )
354 : {
355 0 : delete poSRSClone;
356 0 : FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:UnknownCoordinateSystem");
357 0 : return srs_xml;
358 : }
359 :
360 : /* Set the SpatialReference type attribute correctly for GEOGCS/PROJCS */
361 33 : if ( poSRSClone->IsProjected() )
362 0 : FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:ProjectedCoordinateSystem");
363 : else
364 33 : FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:GeographicCoordinateSystem");
365 :
366 : /* Add the WKT to the XML */
367 33 : char *wkt = NULL;
368 33 : poSRSClone->exportToWkt(&wkt);
369 33 : if (wkt)
370 : {
371 33 : CPLCreateXMLElementAndValue(srs_xml,"WKT", wkt);
372 33 : OGRFree(wkt);
373 : }
374 :
375 : /* Dispose of our close */
376 33 : delete poSRSClone;
377 : }
378 :
379 : /* Handle Origin/Scale/Tolerance */
380 : const char* grid[7] = {
381 : "XOrigin", "YOrigin", "XYScale",
382 : "ZOrigin", "ZScale",
383 35 : "XYTolerance", "ZTolerance" };
384 : const char* gridvalues[7] = {
385 : "-2147483647", "-2147483647", "1000000000",
386 : "-2147483647", "1000000000",
387 35 : "0.0001", "0.0001" };
388 :
389 : /* Convert any layer creation options available, use defaults otherwise */
390 280 : for( int i = 0; i < 7; i++ )
391 : {
392 245 : if ( CSLFetchNameValue( papszOptions, grid[i] ) != NULL )
393 0 : gridvalues[i] = CSLFetchNameValue( papszOptions, grid[i] );
394 :
395 245 : CPLCreateXMLElementAndValue(srs_xml, grid[i], gridvalues[i]);
396 : }
397 :
398 : /* FGDB is always High Precision */
399 35 : CPLCreateXMLElementAndValue(srs_xml, "HighPrecision", "true");
400 :
401 : /* Add the WKID to the XML */
402 35 : if ( wkid )
403 : {
404 33 : CPLCreateXMLElementAndValue(srs_xml, "WKID", wkid);
405 33 : CPLFree(wkid);
406 : }
407 :
408 35 : return srs_xml;
409 : }
410 :
411 : /************************************************************************/
412 : /* CreateFeatureDataset() */
413 : /************************************************************************/
414 :
415 1 : bool FGdbLayer::CreateFeatureDataset(FGdbDataSource* pParentDataSource,
416 : std::string feature_dataset_name,
417 : OGRSpatialReference* poSRS,
418 : char** papszOptions )
419 : {
420 : /* XML node */
421 1 : CPLXMLNode *xml_xml = CPLCreateXMLNode(NULL, CXT_Element, "?xml");
422 1 : FGDB_CPLAddXMLAttribute(xml_xml, "version", "1.0");
423 1 : FGDB_CPLAddXMLAttribute(xml_xml, "encoding", "UTF-8");
424 :
425 : /* First build up a bare-bones feature definition */
426 1 : CPLXMLNode *defn_xml = CPLCreateXMLNode(NULL, CXT_Element, "esri:DataElement");
427 1 : CPLAddXMLSibling(xml_xml, defn_xml);
428 :
429 : /* Add the attributes to the DataElement */
430 1 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
431 1 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xs", "http://www.w3.org/2001/XMLSchema");
432 1 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:esri", "http://www.esri.com/schemas/ArcGIS/10.1");
433 :
434 : /* Need to set this to esri:DEFeatureDataset or esri:DETable */
435 1 : FGDB_CPLAddXMLAttribute(defn_xml, "xsi:type", "esri:DEFeatureDataset");
436 :
437 : /* Add in more children */
438 1 : std::string catalog_page = "\\" + feature_dataset_name;
439 1 : CPLCreateXMLElementAndValue(defn_xml,"CatalogPath", catalog_page.c_str());
440 1 : CPLCreateXMLElementAndValue(defn_xml,"Name", feature_dataset_name.c_str());
441 1 : CPLCreateXMLElementAndValue(defn_xml,"ChildrenExpanded", "false");
442 1 : CPLCreateXMLElementAndValue(defn_xml,"DatasetType", "esriDTFeatureDataset");
443 1 : CPLCreateXMLElementAndValue(defn_xml,"Versioned", "false");
444 1 : CPLCreateXMLElementAndValue(defn_xml,"CanVersion", "false");
445 :
446 : /* Add in empty extent */
447 1 : CPLXMLNode *extent_xml = CPLCreateXMLNode(NULL, CXT_Element, "Extent");
448 1 : FGDB_CPLAddXMLAttribute(extent_xml, "xsi:nil", "true");
449 1 : CPLAddXMLChild(defn_xml, extent_xml);
450 :
451 : /* Add the SRS */
452 : if( TRUE ) // TODO: conditional on existence of SRS
453 : {
454 1 : CPLXMLNode *srs_xml = XMLSpatialReference(poSRS, papszOptions);
455 1 : if ( srs_xml )
456 1 : CPLAddXMLChild(defn_xml, srs_xml);
457 : }
458 :
459 : /* Convert our XML tree into a string for FGDB */
460 1 : char *defn_str = CPLSerializeXMLTree(xml_xml);
461 1 : CPLDestroyXMLNode(xml_xml);
462 :
463 : /* TODO, tie this to debugging levels */
464 1 : CPLDebug("FGDB", "%s", defn_str);
465 :
466 : /* Create the FeatureDataset. */
467 1 : Geodatabase *gdb = pParentDataSource->GetGDB();
468 1 : fgdbError hr = gdb->CreateFeatureDataset(defn_str);
469 :
470 : /* Free the XML */
471 1 : CPLFree(defn_str);
472 :
473 : /* Check table create status */
474 1 : if (FAILED(hr))
475 : {
476 0 : return GDBErr(hr, "Failed at creating FeatureDataset " + feature_dataset_name);
477 : }
478 :
479 1 : return true;
480 : }
481 :
482 : /************************************************************************/
483 : /* Create() */
484 : /* Build up an FGDB XML layer definition and use it to create a Table */
485 : /* or Feature Class to work from. */
486 : /* */
487 : /* Layer creation options: */
488 : /* FEATURE_DATASET, nest layer inside a FeatureDataset folder */
489 : /* GEOMETRY_NAME, user-selected name for the geometry column */
490 : /* OID_NAME, user-selected name for the FID column */
491 : /* XORIGIN, YORIGIN, ZORIGIN, origin of the snapping grid */
492 : /* XYSCALE, ZSCALE, inverse resolution of the snapping grid */
493 : /* XYTOLERANCE, ZTOLERANCE, snapping tolerance for topology/networks */
494 : /* */
495 : /************************************************************************/
496 :
497 17 : bool FGdbLayer::Create(FGdbDataSource* pParentDataSource,
498 : const char* pszLayerName,
499 : OGRSpatialReference* poSRS,
500 : OGRwkbGeometryType eType,
501 : char** papszOptions)
502 : {
503 17 : std::string table_path = "\\" + std::string(pszLayerName);
504 34 : std::string parent_path = "";
505 34 : std::wstring wtable_path, wparent_path;
506 17 : std::string geometry_name = FGDB_GEOMETRY_NAME;
507 34 : std::string fid_name = FGDB_OID_NAME;
508 17 : std::string esri_type;
509 17 : bool has_z = false;
510 :
511 : /* Handle the FEATURE_DATASET case */
512 17 : if ( CSLFetchNameValue( papszOptions, "FEATURE_DATASET") != NULL )
513 : {
514 2 : std::string feature_dataset = CSLFetchNameValue( papszOptions, "FEATURE_DATASET");
515 :
516 : /* Check if FEATURE_DATASET exists. Otherwise create it */
517 2 : std::vector<wstring> featuredatasets;
518 2 : Geodatabase *gdb = pParentDataSource->GetGDB();
519 2 : int bFeatureDataSetExists = FALSE;
520 : fgdbError hr;
521 2 : if ( !FAILED(hr = gdb->GetChildDatasets(L"\\", L"Feature Dataset", featuredatasets)) )
522 : {
523 2 : std::wstring feature_dataset_with_slash = L"\\" + StringToWString(feature_dataset);
524 3 : for ( unsigned int i = 0; i < featuredatasets.size(); i++ )
525 : {
526 1 : if (featuredatasets[i] == feature_dataset_with_slash)
527 1 : bFeatureDataSetExists = TRUE;
528 2 : }
529 : }
530 :
531 2 : if (!bFeatureDataSetExists)
532 : {
533 1 : bool rv = CreateFeatureDataset(pParentDataSource, feature_dataset, poSRS, papszOptions);
534 1 : if ( ! rv )
535 0 : return rv;
536 : }
537 :
538 2 : table_path = "\\" + feature_dataset + table_path;
539 2 : parent_path = "\\" + feature_dataset;
540 : }
541 :
542 : /* Convert table_path into wstring */
543 17 : wtable_path = StringToWString(table_path);
544 17 : wparent_path = StringToWString(parent_path);
545 :
546 : /* Over-ride the geometry name if necessary */
547 17 : if ( CSLFetchNameValue( papszOptions, "GEOMETRY_NAME") != NULL )
548 0 : geometry_name = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME");
549 :
550 : /* Over-ride the OID name if necessary */
551 17 : if ( CSLFetchNameValue( papszOptions, "OID_NAME") != NULL )
552 0 : fid_name = CSLFetchNameValue( papszOptions, "OID_NAME");
553 :
554 : /* Figure out our geometry type */
555 17 : if ( eType != wkbNone )
556 : {
557 17 : if ( wkbFlatten(eType) == wkbUnknown )
558 : {
559 0 : return GDBErr(-1, "FGDB layers cannot be created with a wkbUnknown layer geometry type.");
560 : }
561 17 : if ( ! OGRGeometryToGDB(eType, &esri_type, &has_z) )
562 0 : return GDBErr(-1, "Unable to map OGR type to ESRI type");
563 : }
564 :
565 : /* XML node */
566 17 : CPLXMLNode *xml_xml = CPLCreateXMLNode(NULL, CXT_Element, "?xml");
567 17 : FGDB_CPLAddXMLAttribute(xml_xml, "version", "1.0");
568 17 : FGDB_CPLAddXMLAttribute(xml_xml, "encoding", "UTF-8");
569 :
570 : /* First build up a bare-bones feature definition */
571 17 : CPLXMLNode *defn_xml = CPLCreateXMLNode(NULL, CXT_Element, "esri:DataElement");
572 17 : CPLAddXMLSibling(xml_xml, defn_xml);
573 :
574 : /* Add the attributes to the DataElement */
575 17 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
576 17 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xs", "http://www.w3.org/2001/XMLSchema");
577 17 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:esri", "http://www.esri.com/schemas/ArcGIS/10.1");
578 :
579 : /* Need to set this to esri:DEFeatureDataset or esri:DETable */
580 17 : FGDB_CPLAddXMLAttribute(defn_xml, "xsi:type", (eType == wkbNone ? "esri:DETable" : "esri:DEFeatureClass"));
581 :
582 : /* Add in more children */
583 17 : CPLCreateXMLElementAndValue(defn_xml,"CatalogPath",table_path.c_str());
584 17 : CPLCreateXMLElementAndValue(defn_xml,"Name", pszLayerName);
585 17 : CPLCreateXMLElementAndValue(defn_xml,"ChildrenExpanded", "false");
586 :
587 : /* WKB type of none implies this is a 'Table' otherwise it's a 'Feature Class' */
588 17 : std::string datasettype = (eType == wkbNone ? "esriDTTable" : "esriDTFeatureClass");
589 34 : CPLCreateXMLElementAndValue(defn_xml,"DatasetType", datasettype.c_str() );
590 17 : CPLCreateXMLElementAndValue(defn_xml,"Versioned", "false");
591 17 : CPLCreateXMLElementAndValue(defn_xml,"CanVersion", "false");
592 :
593 : /* We might need to make OID optional later, but OGR likes to have a FID */
594 17 : CPLCreateXMLElementAndValue(defn_xml,"HasOID", "true");
595 17 : CPLCreateXMLElementAndValue(defn_xml,"OIDFieldName", fid_name.c_str());
596 :
597 : /* Add in empty Fields */
598 17 : CPLXMLNode *fields_xml = CPLCreateXMLNode(defn_xml, CXT_Element, "Fields");
599 17 : FGDB_CPLAddXMLAttribute(fields_xml, "xsi:type", "esri:Fields");
600 17 : CPLXMLNode *fieldarray_xml = CPLCreateXMLNode(fields_xml, CXT_Element, "FieldArray");
601 17 : FGDB_CPLAddXMLAttribute(fieldarray_xml, "xsi:type", "esri:ArrayOfField");
602 :
603 : /* Feature Classes have an implicit geometry column, so we'll add it at creation time */
604 17 : if ( eType != wkbNone )
605 : {
606 17 : CPLXMLNode *shape_xml = CPLCreateXMLNode(fieldarray_xml, CXT_Element, "Field");
607 17 : FGDB_CPLAddXMLAttribute(shape_xml, "xsi:type", "esri:Field");
608 17 : CPLCreateXMLElementAndValue(shape_xml, "Name", geometry_name.c_str());
609 17 : CPLCreateXMLElementAndValue(shape_xml, "Type", "esriFieldTypeGeometry");
610 17 : CPLCreateXMLElementAndValue(shape_xml, "IsNullable", "false");
611 17 : CPLCreateXMLElementAndValue(shape_xml, "Length", "0");
612 17 : CPLCreateXMLElementAndValue(shape_xml, "Precision", "0");
613 17 : CPLCreateXMLElementAndValue(shape_xml, "Scale", "0");
614 17 : CPLCreateXMLElementAndValue(shape_xml, "Required", "true");
615 17 : CPLXMLNode *geom_xml = CPLCreateXMLNode(shape_xml, CXT_Element, "GeometryDef");
616 17 : FGDB_CPLAddXMLAttribute(geom_xml, "xsi:type", "esri:GeometryDef");
617 17 : CPLCreateXMLElementAndValue(geom_xml, "AvgNumPoints", "0");
618 17 : CPLCreateXMLElementAndValue(geom_xml, "GeometryType", esri_type.c_str());
619 17 : CPLCreateXMLElementAndValue(geom_xml,"HasM", "false");
620 17 : CPLCreateXMLElementAndValue(geom_xml,"HasZ", (has_z ? "true" : "false"));
621 :
622 : /* Add the SRS if we have one */
623 17 : CPLXMLNode *srs_xml = XMLSpatialReference(poSRS, papszOptions);
624 17 : if ( srs_xml )
625 17 : CPLAddXMLChild(geom_xml, srs_xml);
626 : }
627 :
628 : /* All (?) Tables and Feature Classes will have an ObjectID */
629 17 : CPLXMLNode *oid_xml = CPLCreateXMLNode(fieldarray_xml, CXT_Element, "Field");
630 17 : FGDB_CPLAddXMLAttribute(oid_xml, "xsi:type", "esri:Field");
631 17 : CPLCreateXMLElementAndValue(oid_xml, "Name", fid_name.c_str());
632 17 : CPLCreateXMLElementAndValue(oid_xml, "Type", "esriFieldTypeOID");
633 17 : CPLCreateXMLElementAndValue(oid_xml, "IsNullable", "false");
634 17 : CPLCreateXMLElementAndValue(oid_xml, "Length", "12");
635 17 : CPLCreateXMLElementAndValue(oid_xml, "Precision", "0");
636 17 : CPLCreateXMLElementAndValue(oid_xml, "Scale", "0");
637 17 : CPLCreateXMLElementAndValue(oid_xml, "Required", "true");
638 :
639 : /* Add in empty Indexes */
640 17 : CPLXMLNode *indexes_xml = CPLCreateXMLNode(defn_xml, CXT_Element, "Indexes");
641 17 : FGDB_CPLAddXMLAttribute(indexes_xml, "xsi:type", "esri:Indexes");
642 17 : CPLXMLNode *indexarray_xml = CPLCreateXMLNode(indexes_xml, CXT_Element, "IndexArray");
643 17 : FGDB_CPLAddXMLAttribute(indexarray_xml, "xsi:type", "esri:ArrayOfIndex");
644 :
645 : /* Map from OGR WKB type to ESRI type */
646 17 : if ( eType != wkbNone )
647 : {
648 : /* Declare our feature type */
649 17 : CPLCreateXMLElementAndValue(defn_xml,"FeatureType", "esriFTSimple");
650 17 : CPLCreateXMLElementAndValue(defn_xml,"ShapeType", esri_type.c_str());
651 17 : CPLCreateXMLElementAndValue(defn_xml,"ShapeFieldName", geometry_name.c_str());
652 :
653 : /* Dimensionality */
654 17 : CPLCreateXMLElementAndValue(defn_xml,"HasM", "false");
655 17 : CPLCreateXMLElementAndValue(defn_xml,"HasZ", (has_z ? "true" : "false"));
656 :
657 : /* TODO: Handle spatial indexes (layer creation option?) */
658 17 : CPLCreateXMLElementAndValue(defn_xml,"HasSpatialIndex", "false");
659 :
660 : /* We can't know the extent at this point <Extent xsi:nil='true'/> */
661 17 : CPLXMLNode *extn_xml = CPLCreateXMLNode(defn_xml, CXT_Element, "Extent");
662 17 : FGDB_CPLAddXMLAttribute(extn_xml, "xsi:nil", "true");
663 : }
664 :
665 : /* Feature Class with known SRS gets an SRS entry */
666 17 : if( eType != wkbNone )
667 : {
668 17 : CPLXMLNode *srs_xml = XMLSpatialReference(poSRS, papszOptions);
669 17 : if ( srs_xml )
670 17 : CPLAddXMLChild(defn_xml, srs_xml);
671 : }
672 :
673 : /* Convert our XML tree into a string for FGDB */
674 17 : char *defn_str = CPLSerializeXMLTree(xml_xml);
675 17 : CPLDestroyXMLNode(xml_xml);
676 :
677 : /* TODO, tie this to debugging levels */
678 17 : CPLDebug("FGDB", "%s", defn_str);
679 : //std::cout << defn_str << std::endl;
680 :
681 : /* Create the table. */
682 17 : Table *table = new Table;
683 34 : Geodatabase *gdb = pParentDataSource->GetGDB();
684 17 : fgdbError hr = gdb->CreateTable(defn_str, wparent_path, *table);
685 :
686 : /* Free the XML */
687 17 : CPLFree(defn_str);
688 :
689 : /* Check table create status */
690 17 : if (FAILED(hr))
691 : {
692 0 : delete table;
693 0 : return GDBErr(hr, "Failed at creating table for " + table_path);
694 : }
695 :
696 : /* Store the new FGDB Table pointer and set up the OGRFeatureDefn */
697 17 : return FGdbLayer::Initialize(pParentDataSource, table, wtable_path, L"Table");
698 : }
699 :
700 : /*************************************************************************/
701 : /* Initialize() */
702 : /* Has ownership of the table as soon as it is called. */
703 : /************************************************************************/
704 :
705 81 : bool FGdbLayer::Initialize(FGdbDataSource* pParentDataSource, Table* pTable,
706 : std::wstring wstrTablePath, std::wstring wstrType)
707 : {
708 : long hr;
709 :
710 81 : m_pDS = pParentDataSource; // we never assume ownership of the parent - so our destructor should not delete
711 :
712 81 : m_pTable = pTable;
713 :
714 81 : m_wstrTablePath = wstrTablePath;
715 81 : m_wstrType = wstrType;
716 :
717 81 : wstring wstrQueryName;
718 81 : if (FAILED(hr = pParentDataSource->GetGDB()->GetQueryName(wstrTablePath, wstrQueryName)))
719 : return GDBErr(hr, "Failed at getting underlying table name for " +
720 0 : WStringToString(wstrTablePath));
721 :
722 81 : m_strName = WStringToString(wstrQueryName);
723 :
724 81 : 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
725 : //as long as we use the same compiler & settings in both the ogr build and this
726 : //driver, we should be OK
727 81 : m_pFeatureDefn->Reference();
728 :
729 81 : string tableDef;
730 81 : if (FAILED(hr = m_pTable->GetDefinition(tableDef)))
731 : return GDBErr(hr, "Failed at getting table definition for " +
732 0 : WStringToString(wstrTablePath));
733 :
734 : //xxx printf("Table definition = %s", tableDef.c_str() );
735 :
736 81 : bool abort = false;
737 :
738 : // extract schema information from table
739 81 : CPLXMLNode *psRoot = CPLParseXMLString( tableDef.c_str() );
740 :
741 81 : if (psRoot == NULL)
742 : {
743 : CPLError( CE_Failure, CPLE_AppDefined, "%s",
744 0 : ("Failed parsing GDB Table Schema XML for " + m_strName).c_str());
745 0 : return false;
746 : }
747 :
748 81 : CPLXMLNode *pDataElementNode = psRoot->psNext; // Move to next field which should be DataElement
749 :
750 81 : if( pDataElementNode != NULL
751 : && pDataElementNode->psChild != NULL
752 : && pDataElementNode->eType == CXT_Element
753 : && EQUAL(pDataElementNode->pszValue,"esri:DataElement") )
754 : {
755 : CPLXMLNode *psNode;
756 :
757 1944 : for( psNode = pDataElementNode->psChild;
758 : psNode != NULL;
759 : psNode = psNode->psNext )
760 : {
761 1863 : if( psNode->eType == CXT_Element && psNode->psChild != NULL )
762 : {
763 1539 : if (EQUAL(psNode->pszValue,"OIDFieldName") )
764 : {
765 : char* pszUnescaped = CPLUnescapeString(
766 81 : psNode->psChild->pszValue, NULL, CPLES_XML);
767 81 : m_strOIDFieldName = pszUnescaped;
768 81 : CPLFree(pszUnescaped);
769 : }
770 1458 : else if (EQUAL(psNode->pszValue,"ShapeFieldName") )
771 : {
772 : char* pszUnescaped = CPLUnescapeString(
773 81 : psNode->psChild->pszValue, NULL, CPLES_XML);
774 81 : m_strShapeFieldName = pszUnescaped;
775 81 : CPLFree(pszUnescaped);
776 : }
777 1377 : else if (EQUAL(psNode->pszValue,"Fields") )
778 : {
779 81 : if (!GDBToOGRFields(psNode))
780 : {
781 0 : abort = true;
782 0 : break;
783 : }
784 : }
785 : }
786 : }
787 :
788 81 : if (m_strShapeFieldName.size() == 0)
789 0 : m_pFeatureDefn->SetGeomType(wkbNone);
790 : }
791 : else
792 : {
793 : CPLError( CE_Failure, CPLE_AppDefined, "%s",
794 0 : ("Failed parsing GDB Table Schema XML (DataElement) for " + m_strName).c_str());
795 0 : return false;
796 : }
797 81 : CPLDestroyXMLNode( psRoot );
798 :
799 81 : if (abort)
800 0 : return false;
801 :
802 81 : return true; //AOToOGRFields(ipFields, m_pFeatureDefn, m_vOGRFieldToESRIField);
803 : }
804 :
805 : /************************************************************************/
806 : /* ParseGeometryDef() */
807 : /************************************************************************/
808 :
809 81 : bool FGdbLayer::ParseGeometryDef(CPLXMLNode* psRoot)
810 : {
811 : CPLXMLNode *psGeometryDefItem;
812 :
813 81 : string geometryType;
814 81 : bool hasZ = false;
815 81 : string wkt, wkid;
816 :
817 648 : for (psGeometryDefItem = psRoot->psChild;
818 : psGeometryDefItem != NULL;
819 : psGeometryDefItem = psGeometryDefItem->psNext )
820 : {
821 : //loop through all "GeometryDef" elements
822 : //
823 :
824 567 : if (psGeometryDefItem->eType == CXT_Element &&
825 : psGeometryDefItem->psChild != NULL)
826 : {
827 486 : if (EQUAL(psGeometryDefItem->pszValue,"GeometryType"))
828 : {
829 : char* pszUnescaped = CPLUnescapeString(
830 81 : psGeometryDefItem->psChild->pszValue, NULL, CPLES_XML);
831 :
832 81 : geometryType = pszUnescaped;
833 :
834 81 : CPLFree(pszUnescaped);
835 : }
836 405 : else if (EQUAL(psGeometryDefItem->pszValue,"SpatialReference"))
837 : {
838 81 : ParseSpatialReference(psGeometryDefItem, &wkt, &wkid); // we don't check for success because it
839 : // may not be there
840 : }
841 : /* No M support in OGR yet
842 : else if (EQUAL(psFieldNode->pszValue,"HasM")
843 : {
844 : char* pszUnescaped = CPLUnescapeString(psNode->psChild->pszValue, NULL, CPLES_XML);
845 :
846 : if (!strcmp(szUnescaped, "true"))
847 : hasM = true;
848 :
849 : CPLFree(pszUnescaped);
850 : }
851 : */
852 324 : else if (EQUAL(psGeometryDefItem->pszValue,"HasZ"))
853 : {
854 : char* pszUnescaped = CPLUnescapeString(
855 81 : psGeometryDefItem->psChild->pszValue, NULL, CPLES_XML);
856 :
857 81 : if (!strcmp(pszUnescaped, "true"))
858 36 : hasZ = true;
859 :
860 81 : CPLFree(pszUnescaped);
861 : }
862 : }
863 :
864 : }
865 :
866 : OGRwkbGeometryType ogrGeoType;
867 81 : if (!GDBToOGRGeometry(geometryType, hasZ, &ogrGeoType))
868 0 : return false;
869 :
870 81 : m_pFeatureDefn->SetGeomType(ogrGeoType);
871 :
872 81 : if (wkbFlatten(ogrGeoType) == wkbMultiLineString ||
873 : wkbFlatten(ogrGeoType) == wkbMultiPoint)
874 36 : m_forceMulti = true;
875 :
876 81 : if (wkid.length() > 0)
877 : {
878 79 : m_pSRS = new OGRSpatialReference();
879 158 : if (m_pSRS->importFromEPSG(atoi(wkid.c_str())) != OGRERR_NONE)
880 : {
881 0 : delete m_pSRS;
882 0 : m_pSRS = NULL;
883 : }
884 : else
885 79 : return true;
886 : }
887 :
888 2 : if (wkt.length() > 0)
889 : {
890 0 : if (!GDBToOGRSpatialReference(wkt, &m_pSRS))
891 : {
892 : //report error, but be passive about it
893 : CPLError( CE_Warning, CPLE_AppDefined,
894 0 : "Failed Mapping ESRI Spatial Reference");
895 : }
896 : }
897 : else
898 : {
899 : //report error, but be passive about it
900 2 : CPLError( CE_Warning, CPLE_AppDefined, "Empty Spatial Reference");
901 : }
902 :
903 2 : return true;
904 : }
905 :
906 : /************************************************************************/
907 : /* ParseSpatialReference() */
908 : /************************************************************************/
909 :
910 81 : bool FGdbLayer::ParseSpatialReference(CPLXMLNode* psSpatialRefNode,
911 : string* pOutWkt, string* pOutWKID)
912 : {
913 81 : *pOutWkt = "";
914 81 : *pOutWKID = "";
915 :
916 : CPLXMLNode* psSRItemNode;
917 :
918 : /* Loop through all the SRS elements we want to store */
919 968 : for( psSRItemNode = psSpatialRefNode->psChild;
920 : psSRItemNode != NULL;
921 : psSRItemNode = psSRItemNode->psNext )
922 : {
923 : /* The WKID maps (mostly) to an EPSG code */
924 966 : if( psSRItemNode->eType == CXT_Element &&
925 : psSRItemNode->psChild != NULL &&
926 : EQUAL(psSRItemNode->pszValue,"WKID") )
927 : {
928 79 : char* pszUnescaped = CPLUnescapeString(psSRItemNode->psChild->pszValue, NULL, CPLES_XML);
929 79 : *pOutWKID = pszUnescaped;
930 79 : CPLFree(pszUnescaped);
931 : }
932 : /* The WKT well-known text can be converted by OGR */
933 808 : else if( psSRItemNode->eType == CXT_Element &&
934 : psSRItemNode->psChild != NULL &&
935 : EQUAL(psSRItemNode->pszValue,"WKT") )
936 : {
937 79 : char* pszUnescaped = CPLUnescapeString(psSRItemNode->psChild->pszValue, NULL, CPLES_XML);
938 79 : *pOutWkt = pszUnescaped;
939 79 : CPLFree(pszUnescaped);
940 : }
941 :
942 : }
943 81 : return (*pOutWkt != "" || *pOutWKID != "");
944 : }
945 :
946 : /************************************************************************/
947 : /* GDBToOGRFields() */
948 : /************************************************************************/
949 :
950 81 : bool FGdbLayer::GDBToOGRFields(CPLXMLNode* psRoot)
951 : {
952 81 : m_vOGRFieldToESRIField.clear();
953 :
954 81 : if (psRoot->psChild == NULL || psRoot->psChild->psNext == NULL)
955 : {
956 0 : CPLError( CE_Failure, CPLE_AppDefined, "Unrecognized GDB XML Schema");
957 :
958 0 : return false;
959 : }
960 :
961 81 : psRoot = psRoot->psChild->psNext; //change root to "FieldArray"
962 :
963 : //CPLAssert(ogrToESRIFieldMapping.size() == pOGRFeatureDef->GetFieldCount());
964 :
965 : CPLXMLNode* psFieldNode;
966 :
967 508 : for( psFieldNode = psRoot->psChild;
968 : psFieldNode != NULL;
969 : psFieldNode = psFieldNode->psNext )
970 : {
971 : //loop through all "Field" elements
972 : //
973 :
974 427 : if( psFieldNode->eType == CXT_Element && psFieldNode->psChild != NULL &&
975 : EQUAL(psFieldNode->pszValue,"Field"))
976 : {
977 :
978 : CPLXMLNode* psFieldItemNode;
979 346 : std::string fieldName;
980 346 : std::string fieldType;
981 346 : int nLength = 0;
982 346 : int nPrecision = 0;
983 :
984 : // loop through all items in Field element
985 : //
986 :
987 3784 : for( psFieldItemNode = psFieldNode->psChild;
988 : psFieldItemNode != NULL;
989 : psFieldItemNode = psFieldItemNode->psNext )
990 : {
991 3438 : if (psFieldItemNode->eType == CXT_Element)
992 : {
993 :
994 3092 : if (EQUAL(psFieldItemNode->pszValue,"Name"))
995 : {
996 : char* pszUnescaped = CPLUnescapeString(
997 346 : psFieldItemNode->psChild->pszValue, NULL, CPLES_XML);
998 346 : fieldName = pszUnescaped;
999 346 : CPLFree(pszUnescaped);
1000 : }
1001 2746 : else if (EQUAL(psFieldItemNode->pszValue,"Type") )
1002 : {
1003 : char* pszUnescaped = CPLUnescapeString(
1004 346 : psFieldItemNode->psChild->pszValue, NULL, CPLES_XML);
1005 346 : fieldType = pszUnescaped;
1006 346 : CPLFree(pszUnescaped);
1007 : }
1008 2400 : else if (EQUAL(psFieldItemNode->pszValue,"GeometryDef") )
1009 : {
1010 81 : if (!ParseGeometryDef(psFieldItemNode))
1011 0 : return false; // if we failed parsing the GeometryDef, we are done!
1012 : }
1013 2319 : else if (EQUAL(psFieldItemNode->pszValue,"Length") )
1014 : {
1015 346 : nLength = atoi(psFieldItemNode->psChild->pszValue);
1016 : }
1017 1973 : else if (EQUAL(psFieldItemNode->pszValue,"Precision") )
1018 : {
1019 346 : nPrecision = atoi(psFieldItemNode->psChild->pszValue);
1020 : }
1021 : }
1022 : }
1023 :
1024 :
1025 : ///////////////////////////////////////////////////////////////////
1026 : // At this point we have parsed everything about the current field
1027 :
1028 :
1029 346 : if (fieldType == "esriFieldTypeGeometry")
1030 : {
1031 81 : m_strShapeFieldName = fieldName;
1032 :
1033 81 : continue; // finish here for special field - don't add as OGR fielddef
1034 : }
1035 265 : else if (fieldType == "esriFieldTypeOID")
1036 : {
1037 : //m_strOIDFieldName = fieldName; // already set by this point
1038 :
1039 81 : continue; // finish here for special field - don't add as OGR fielddef
1040 : }
1041 :
1042 : OGRFieldType ogrType;
1043 : //CPLDebug("FGDB", "name = %s, type = %s", fieldName.c_str(), fieldType.c_str() );
1044 184 : if (!GDBToOGRFieldType(fieldType, &ogrType))
1045 : {
1046 : // field cannot be mapped, skipping further processing
1047 : CPLError( CE_Warning, CPLE_AppDefined, "Skipping field: [%s] type: [%s] ",
1048 0 : fieldName.c_str(), fieldType.c_str() );
1049 0 : continue;
1050 : }
1051 :
1052 :
1053 : //TODO: Optimization - modify m_wstrSubFields so it only fetches fields that are mapped
1054 :
1055 184 : OGRFieldDefn fieldTemplate( fieldName.c_str(), ogrType);
1056 : //fieldTemplate.SetWidth(nLength);
1057 : //fieldTemplate.SetPrecision(nPrecision);
1058 184 : m_pFeatureDefn->AddFieldDefn( &fieldTemplate );
1059 :
1060 184 : m_vOGRFieldToESRIField.push_back(StringToWString(fieldName));
1061 184 : m_vOGRFieldToESRIFieldType.push_back( fieldType );
1062 :
1063 : }
1064 : }
1065 :
1066 81 : return true;
1067 : }
1068 :
1069 :
1070 : /************************************************************************/
1071 : /* ResetReading() */
1072 : /************************************************************************/
1073 :
1074 273 : void FGdbLayer::ResetReading()
1075 : {
1076 : long hr;
1077 :
1078 273 : if (m_pOGRFilterGeometry && !m_pOGRFilterGeometry->IsEmpty())
1079 : {
1080 : // Search spatial
1081 : // As of beta1, FileGDB only supports bbox searched, if we have GEOS installed,
1082 : // we can do the rest ourselves.
1083 :
1084 78 : OGREnvelope ogrEnv;
1085 :
1086 78 : m_pOGRFilterGeometry->getEnvelope(&ogrEnv);
1087 :
1088 : //spatial query
1089 78 : FileGDBAPI::Envelope env(ogrEnv.MinX, ogrEnv.MaxX, ogrEnv.MinY, ogrEnv.MaxY);
1090 :
1091 78 : if FAILED(hr = m_pTable->Search(m_wstrSubfields, m_wstrWhereClause, env, true, *m_pEnumRows))
1092 0 : GDBErr(hr, "Failed Searching");
1093 :
1094 78 : m_bFilterDirty = false;
1095 :
1096 78 : return;
1097 : }
1098 :
1099 : // Search non-spatial
1100 :
1101 195 : if FAILED(hr = m_pTable->Search(m_wstrSubfields, m_wstrWhereClause, true, *m_pEnumRows))
1102 0 : GDBErr(hr, "Failed Searching");
1103 :
1104 195 : m_bFilterDirty = false;
1105 :
1106 : }
1107 :
1108 : /************************************************************************/
1109 : /* SetSpatialFilter() */
1110 : /************************************************************************/
1111 :
1112 104 : void FGdbLayer::SetSpatialFilter( OGRGeometry* pOGRGeom )
1113 : {
1114 104 : if (m_pOGRFilterGeometry)
1115 : {
1116 26 : OGRGeometryFactory::destroyGeometry(m_pOGRFilterGeometry);
1117 26 : m_pOGRFilterGeometry = NULL;
1118 : }
1119 :
1120 104 : if (pOGRGeom == NULL || pOGRGeom->IsEmpty())
1121 : {
1122 78 : m_bFilterDirty = true;
1123 :
1124 78 : return;
1125 : }
1126 :
1127 26 : m_pOGRFilterGeometry = pOGRGeom->clone();
1128 :
1129 26 : m_pOGRFilterGeometry->transformTo(m_pSRS);
1130 :
1131 26 : m_bFilterDirty = true;
1132 : }
1133 :
1134 : /************************************************************************/
1135 : /* SetSpatialFilterRect() */
1136 : /************************************************************************/
1137 :
1138 0 : void FGdbLayer::SetSpatialFilterRect (double dfMinX, double dfMinY, double dfMaxX, double dfMaxY)
1139 : {
1140 :
1141 : //TODO: can optimize this by changing how the filter gets generated -
1142 : //this will work for now
1143 :
1144 0 : OGRGeometry* pTemp = OGRGeometryFactory::createGeometry(wkbPolygon);
1145 :
1146 0 : pTemp->assignSpatialReference(m_pSRS);
1147 :
1148 0 : OGRLinearRing ring;
1149 :
1150 0 : ring.addPoint( dfMinX, dfMinY );
1151 0 : ring.addPoint( dfMinX, dfMaxY );
1152 0 : ring.addPoint( dfMaxX, dfMaxY );
1153 0 : ring.addPoint( dfMaxX, dfMinY );
1154 0 : ring.addPoint( dfMinX, dfMinY );
1155 0 : ((OGRPolygon *) pTemp)->addRing( &ring );
1156 :
1157 0 : SetSpatialFilter(pTemp);
1158 :
1159 0 : OGRGeometryFactory::destroyGeometry(pTemp);
1160 0 : }
1161 :
1162 :
1163 : /************************************************************************/
1164 : /* SetAttributeFilter() */
1165 : /************************************************************************/
1166 :
1167 91 : OGRErr FGdbLayer::SetAttributeFilter( const char* pszQuery )
1168 : {
1169 91 : m_wstrWhereClause = StringToWString( (pszQuery != NULL) ? pszQuery : "" );
1170 :
1171 91 : m_bFilterDirty = true;
1172 :
1173 91 : return OGRERR_NONE;
1174 : }
1175 :
1176 : /************************************************************************/
1177 : /* OGRFeatureFromGdbRow() */
1178 : /************************************************************************/
1179 :
1180 192 : bool FGdbLayer::OGRFeatureFromGdbRow(Row* pRow, OGRFeature** ppFeature)
1181 : {
1182 : long hr;
1183 :
1184 192 : OGRFeature* pOutFeature = new OGRFeature(m_pFeatureDefn);
1185 :
1186 : /////////////////////////////////////////////////////////
1187 : // Translate OID
1188 : //
1189 :
1190 192 : int32 oid = -1;
1191 192 : if (FAILED(hr = pRow->GetOID(oid)))
1192 : {
1193 : //this should never happen
1194 0 : delete pOutFeature;
1195 0 : return false;
1196 : }
1197 192 : pOutFeature->SetFID(oid);
1198 :
1199 :
1200 : /////////////////////////////////////////////////////////
1201 : // Translate Geometry
1202 : //
1203 :
1204 192 : ShapeBuffer gdbGeometry;
1205 192 : if (!FAILED(hr = pRow->GetGeometry(gdbGeometry)))
1206 : {
1207 192 : OGRGeometry* pOGRGeo = NULL;
1208 :
1209 192 : if ((!GDBGeometryToOGRGeometry(m_forceMulti, &gdbGeometry, m_pSRS, &pOGRGeo)) || pOGRGeo == NULL)
1210 : {
1211 0 : delete pOutFeature;
1212 0 : return GDBErr(hr, "Failed to translate FileGDB Geometry to OGR Geometry for row " + string(CPLSPrintf("%d", (int)oid)));
1213 : }
1214 :
1215 192 : pOutFeature->SetGeometryDirectly(pOGRGeo);
1216 : }
1217 :
1218 :
1219 : //////////////////////////////////////////////////////////
1220 : // Map fields
1221 : //
1222 :
1223 :
1224 192 : size_t mappedFieldCount = m_vOGRFieldToESRIField.size();
1225 :
1226 192 : bool foundBadColumn = false;
1227 :
1228 768 : for (size_t i = 0; i < mappedFieldCount; ++i)
1229 : {
1230 576 : const wstring & wstrFieldName = m_vOGRFieldToESRIField[i];
1231 576 : const std::string & strFieldType = m_vOGRFieldToESRIFieldType[i];
1232 :
1233 576 : bool isNull = false;
1234 :
1235 576 : if (FAILED(hr = pRow->IsNull(wstrFieldName, isNull)))
1236 : {
1237 : GDBErr(hr, "Failed to determine NULL status from column " +
1238 0 : WStringToString(wstrFieldName));
1239 0 : foundBadColumn = true;
1240 0 : continue;
1241 : }
1242 :
1243 576 : if (isNull)
1244 : {
1245 0 : continue; //leave as unset
1246 : }
1247 :
1248 : //
1249 : // NOTE: This switch statement needs to be kept in sync with GDBToOGRFieldType utility function
1250 : // since we are only checking for types we mapped in that utility function
1251 :
1252 576 : switch (m_pFeatureDefn->GetFieldDefn(i)->GetType())
1253 : {
1254 :
1255 : case OFTInteger:
1256 : {
1257 : int32 val;
1258 :
1259 120 : if (FAILED(hr = pRow->GetInteger(wstrFieldName, val)))
1260 : {
1261 : int16 shortval;
1262 0 : if (FAILED(hr = pRow->GetShort(wstrFieldName, shortval)))
1263 : {
1264 : GDBErr(hr, "Failed to determine integer value for column " +
1265 0 : WStringToString(wstrFieldName));
1266 0 : foundBadColumn = true;
1267 0 : continue;
1268 : }
1269 0 : val = shortval;
1270 : }
1271 :
1272 120 : pOutFeature->SetField(i, (int)val);
1273 : }
1274 120 : break;
1275 :
1276 : case OFTReal:
1277 : {
1278 264 : if (strFieldType == "esriFieldTypeSingle")
1279 : {
1280 : float val;
1281 :
1282 0 : if (FAILED(hr = pRow->GetFloat(wstrFieldName, val)))
1283 : {
1284 : GDBErr(hr, "Failed to determine float value for column " +
1285 0 : WStringToString(wstrFieldName));
1286 0 : foundBadColumn = true;
1287 0 : continue;
1288 : }
1289 :
1290 0 : pOutFeature->SetField(i, val);
1291 : }
1292 : else
1293 : {
1294 : double val;
1295 :
1296 264 : if (FAILED(hr = pRow->GetDouble(wstrFieldName, val)))
1297 : {
1298 : GDBErr(hr, "Failed to determine real value for column " +
1299 0 : WStringToString(wstrFieldName));
1300 0 : foundBadColumn = true;
1301 0 : continue;
1302 : }
1303 :
1304 264 : pOutFeature->SetField(i, val);
1305 : }
1306 : }
1307 264 : break;
1308 : case OFTString:
1309 : {
1310 192 : wstring val;
1311 :
1312 192 : if (FAILED(hr = pRow->GetString(wstrFieldName, val)))
1313 : {
1314 : GDBErr(hr, "Failed to determine string value for column " +
1315 0 : WStringToString(wstrFieldName));
1316 0 : foundBadColumn = true;
1317 0 : continue;
1318 : }
1319 :
1320 192 : pOutFeature->SetField(i, WStringToString(val).c_str());
1321 : }
1322 192 : break;
1323 :
1324 : /* TODO: Need to get test dataset to implement these leave it as NULL for now
1325 : case OFTBinary:
1326 : {
1327 : ByteArray binaryBuf;
1328 :
1329 : if (FAILED(hr = pRow->GetBinary(wstrFieldName, binaryBuf)))
1330 : {
1331 : GDBErr(hr, "Failed to determine binary value for column " + WStringToString(wstrFieldName));
1332 : foundBadColumn = true;
1333 : continue;
1334 : }
1335 :
1336 : pOutFeature->SetField(i, (int)binaryBuf.inUseLength, (GByte*)binaryBuf.byteArray);
1337 : }
1338 : break;
1339 : */
1340 :
1341 : case OFTDateTime:
1342 : {
1343 : struct tm val;
1344 :
1345 0 : if (FAILED(hr = pRow->GetDate(wstrFieldName, val)))
1346 : {
1347 : GDBErr(hr, "Failed to determine date value for column " +
1348 0 : WStringToString(wstrFieldName));
1349 0 : foundBadColumn = true;
1350 0 : continue;
1351 : }
1352 :
1353 : pOutFeature->SetField(i, val.tm_year + 1900, val.tm_mon + 1,
1354 0 : val.tm_mday, val.tm_hour, val.tm_min, val.tm_sec);
1355 : // Examine test data to figure out how to extract that
1356 : }
1357 0 : break;
1358 :
1359 : default:
1360 : {
1361 0 : if (!m_supressColumnMappingError)
1362 : {
1363 0 : foundBadColumn = true;
1364 : CPLError( CE_Warning, CPLE_AppDefined,
1365 : "Row id: %d col:%d has unhandled col type (%d). Setting to NULL.",
1366 0 : (int)oid, (int)i, m_pFeatureDefn->GetFieldDefn(i)->GetType());
1367 : }
1368 : }
1369 : }
1370 : }
1371 :
1372 192 : if (foundBadColumn)
1373 0 : m_supressColumnMappingError = true;
1374 :
1375 :
1376 192 : *ppFeature = pOutFeature;
1377 :
1378 192 : return true;
1379 : }
1380 :
1381 :
1382 : /************************************************************************/
1383 : /* GetNextFeature() */
1384 : /************************************************************************/
1385 :
1386 307 : OGRFeature* FGdbLayer::GetNextFeature()
1387 : {
1388 307 : if (m_bFilterDirty)
1389 12 : ResetReading();
1390 :
1391 :
1392 0 : while (1) //want to skip errors
1393 : {
1394 307 : if (m_pEnumRows == NULL)
1395 0 : return NULL;
1396 :
1397 : long hr;
1398 :
1399 307 : Row row;
1400 :
1401 307 : if (FAILED(hr = m_pEnumRows->Next(row)))
1402 : {
1403 0 : GDBErr(hr, "Failed fetching features");
1404 0 : return NULL;
1405 : }
1406 :
1407 307 : if (hr != S_OK)
1408 : {
1409 : // It's OK, we are done fetching - failure is catched by FAILED macro
1410 117 : return NULL;
1411 : }
1412 :
1413 190 : OGRFeature* pOGRFeature = NULL;
1414 :
1415 190 : if (!OGRFeatureFromGdbRow(&row, &pOGRFeature))
1416 : {
1417 0 : int32 oid = -1;
1418 0 : row.GetOID(oid);
1419 :
1420 0 : GDBErr(hr, CPLSPrintf("Failed translating FGDB row [%d] to OGR Feature", oid));
1421 :
1422 : //return NULL;
1423 0 : continue; //skip feature
1424 : }
1425 :
1426 190 : return pOGRFeature;
1427 : }
1428 : }
1429 :
1430 : /************************************************************************/
1431 : /* GetFeature() */
1432 : /************************************************************************/
1433 :
1434 2 : OGRFeature *FGdbLayer::GetFeature( long oid )
1435 : {
1436 : // do query to fetch individual row
1437 :
1438 : long hr;
1439 2 : Row row;
1440 2 : EnumRows enumRows;
1441 2 : CPLString osQuery;
1442 :
1443 2 : osQuery.Printf("%s = %ld", m_strOIDFieldName.c_str(), oid);
1444 :
1445 2 : if (FAILED(hr = m_pTable->Search(m_wstrSubfields, StringToWString(osQuery.c_str()), true, enumRows)))
1446 : {
1447 0 : GDBErr(hr, "Failed fetching row ");
1448 0 : return NULL;
1449 : }
1450 :
1451 2 : if (FAILED(hr = enumRows.Next(row)))
1452 : {
1453 0 : GDBErr(hr, "Failed fetching row ");
1454 0 : return NULL;
1455 : }
1456 :
1457 2 : if (hr != S_OK)
1458 0 : return NULL; //none found - but no failure
1459 :
1460 :
1461 2 : OGRFeature* pOGRFeature = NULL;
1462 :
1463 2 : if (!OGRFeatureFromGdbRow(&row, &pOGRFeature))
1464 : {
1465 0 : GDBErr(hr, "Failed translating ArcObjects row to OGR Feature");
1466 0 : return NULL;
1467 : }
1468 :
1469 2 : return pOGRFeature;
1470 : }
1471 :
1472 :
1473 : /************************************************************************/
1474 : /* GetFeatureCount() */
1475 : /************************************************************************/
1476 :
1477 130 : int FGdbLayer::GetFeatureCount( int bForce )
1478 : {
1479 : long hr;
1480 130 : int32 rowCount = 0;
1481 :
1482 130 : if (m_pOGRFilterGeometry != NULL || m_wstrWhereClause.size() != 0)
1483 52 : return OGRLayer::GetFeatureCount(bForce);
1484 :
1485 78 : if (FAILED(hr = m_pTable->GetRowCount(rowCount)))
1486 : {
1487 0 : GDBErr(hr, "Failed counting rows");
1488 0 : return 0;
1489 : }
1490 :
1491 : #if 0
1492 : Row row;
1493 : EnumRows enumRows;
1494 :
1495 : if (FAILED(hr = m_pTable->Search(StringToWString(m_strOIDFieldName), L"", true, enumRows)))
1496 : {
1497 : GDBErr(hr, "Failed counting rows");
1498 : return -1;
1499 : }
1500 :
1501 : while (S_OK == (hr = enumRows.Next(row)))
1502 : ++rowCount;
1503 :
1504 : if (FAILED(hr))
1505 : {
1506 : GDBErr(hr, "Failed counting rows (during fetch)");
1507 : return -1;
1508 : }
1509 : #endif
1510 :
1511 78 : return static_cast<int>(rowCount);
1512 : }
1513 :
1514 :
1515 :
1516 : /************************************************************************/
1517 : /* GetExtent() */
1518 : /************************************************************************/
1519 :
1520 13 : OGRErr FGdbLayer::GetExtent (OGREnvelope* psExtent, int bForce)
1521 : {
1522 13 : if (m_pOGRFilterGeometry != NULL || m_wstrWhereClause.size() != 0 ||
1523 : m_strShapeFieldName.size() == 0)
1524 0 : return OGRLayer::GetExtent(psExtent, bForce);
1525 :
1526 : long hr;
1527 13 : Envelope envelope;
1528 13 : if (FAILED(hr = m_pTable->GetExtent(envelope)))
1529 : {
1530 0 : GDBErr(hr, "Failed fetching extent");
1531 0 : return OGRERR_FAILURE;
1532 : }
1533 :
1534 13 : psExtent->MinX = envelope.xMin;
1535 13 : psExtent->MinY = envelope.yMin;
1536 13 : psExtent->MaxX = envelope.xMax;
1537 13 : psExtent->MaxY = envelope.yMax;
1538 :
1539 13 : if (CPLIsNan(psExtent->MinX) ||
1540 : CPLIsNan(psExtent->MinY) ||
1541 : CPLIsNan(psExtent->MaxX) ||
1542 : CPLIsNan(psExtent->MaxY))
1543 0 : return OGRERR_FAILURE;
1544 :
1545 13 : return OGRERR_NONE;
1546 : }
1547 :
1548 : /* OGRErr FGdbLayer::StartTransaction ()
1549 : {
1550 : if ( ! m_pTable )
1551 : return OGRERR_FAILURE;
1552 :
1553 : m_pTable->LoadOnlyMode(true);
1554 : m_pTable->SetWriteLock();
1555 : return OGRERR_NONE;
1556 :
1557 : } */
1558 :
1559 :
1560 : /* OGRErr FGdbLayer::CommitTransaction ()
1561 : {
1562 : if ( ! m_pTable )
1563 : return OGRERR_FAILURE;
1564 :
1565 : m_pTable->LoadOnlyMode(false);
1566 : m_pTable->FreeWriteLock();
1567 : return OGRERR_NONE;
1568 : } */
1569 :
1570 : /* OGRErr FGdbLayer::RollbackTransaction ()
1571 : {
1572 : if ( ! m_pTable )
1573 : return OGRERR_FAILURE;
1574 :
1575 : m_pTable->LoadOnlyMode(false);
1576 : m_pTable->FreeWriteLock();
1577 : return OGRERR_NONE;
1578 : } */
1579 :
1580 :
1581 : /************************************************************************/
1582 : /* GetLayerXML() */
1583 : /* Return XML definition of the Layer as provided by FGDB. Caller must */
1584 : /* free result. */
1585 : /* Not currently used by the driver, but can be used by external code */
1586 : /* for specific purposes. */
1587 : /************************************************************************/
1588 :
1589 0 : OGRErr FGdbLayer::GetLayerXML (char **ppXml)
1590 : {
1591 : long hr;
1592 0 : std::string xml;
1593 :
1594 0 : if ( FAILED(hr = m_pTable->GetDefinition(xml)) )
1595 : {
1596 0 : GDBErr(hr, "Failed fetching XML table definition");
1597 0 : return OGRERR_FAILURE;
1598 : }
1599 :
1600 0 : *ppXml = CPLStrdup(xml.c_str());
1601 0 : return OGRERR_NONE;
1602 : }
1603 :
1604 : /************************************************************************/
1605 : /* GetLayerMetadataXML() */
1606 : /* Return XML metadata for the Layer as provided by FGDB. Caller must */
1607 : /* free result. */
1608 : /* Not currently used by the driver, but can be used by external code */
1609 : /* for specific purposes. */
1610 : /************************************************************************/
1611 :
1612 0 : OGRErr FGdbLayer::GetLayerMetadataXML (char **ppXml)
1613 : {
1614 : long hr;
1615 0 : std::string xml;
1616 :
1617 0 : if ( FAILED(hr = m_pTable->GetDocumentation(xml)) )
1618 : {
1619 0 : GDBErr(hr, "Failed fetching XML table metadata");
1620 0 : return OGRERR_FAILURE;
1621 : }
1622 :
1623 0 : *ppXml = CPLStrdup(xml.c_str());
1624 0 : return OGRERR_NONE;
1625 : }
1626 :
1627 : /************************************************************************/
1628 : /* TestCapability() */
1629 : /************************************************************************/
1630 :
1631 52 : int FGdbLayer::TestCapability( const char* pszCap )
1632 : {
1633 :
1634 52 : if (EQUAL(pszCap,OLCRandomRead))
1635 13 : return TRUE;
1636 :
1637 39 : else if (EQUAL(pszCap,OLCFastFeatureCount))
1638 0 : return m_pOGRFilterGeometry == NULL && m_wstrWhereClause.size() == 0;
1639 :
1640 39 : else if (EQUAL(pszCap,OLCFastSpatialFilter))
1641 0 : return TRUE;
1642 :
1643 39 : else if (EQUAL(pszCap,OLCFastGetExtent))
1644 0 : return m_pOGRFilterGeometry == NULL && m_wstrWhereClause.size() == 0;
1645 :
1646 39 : else if (EQUAL(pszCap,OLCCreateField)) /* CreateField() */
1647 0 : return TRUE;
1648 :
1649 39 : else if (EQUAL(pszCap,OLCSequentialWrite)) /* CreateFeature() */
1650 0 : return TRUE;
1651 :
1652 39 : else if (EQUAL(pszCap,OLCStringsAsUTF8)) /* Native UTF16, converted to UTF8 */
1653 13 : return TRUE;
1654 :
1655 26 : else if (EQUAL(pszCap,OLCReorderFields)) /* TBD ReorderFields() */
1656 0 : return FALSE;
1657 :
1658 26 : else if (EQUAL(pszCap,OLCDeleteFeature)) /* TBD DeleteFeature() */
1659 0 : return FALSE;
1660 :
1661 26 : else if (EQUAL(pszCap,OLCRandomWrite)) /* TBD SetFeature() */
1662 13 : return FALSE;
1663 :
1664 13 : else if (EQUAL(pszCap,OLCDeleteField)) /* TBD DeleteField() */
1665 0 : return FALSE;
1666 :
1667 13 : else if (EQUAL(pszCap,OLCFastSetNextByIndex)) /* TBD FastSetNextByIndex() */
1668 13 : return FALSE;
1669 :
1670 0 : else if (EQUAL(pszCap,OLCTransactions)) /* TBD Start/End Transactions() */
1671 0 : return FALSE;
1672 :
1673 : else
1674 0 : return FALSE;
1675 1947 : }
1676 :
|