1 : /******************************************************************************
2 : * $Id: FGdbUtils.cpp 23984 2012-02-15 05:19:25Z rcoup $
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Different utility functions used in FileGDB OGR driver.
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 "FGdbUtils.h"
33 : #include <algorithm>
34 :
35 : #include "ogr_api.h"
36 : #include "ogrpgeogeometry.h"
37 :
38 : CPL_CVSID("$Id: FGdbUtils.cpp 23984 2012-02-15 05:19:25Z rcoup $");
39 :
40 : using std::string;
41 :
42 : /*************************************************************************/
43 : /* StringToWString() */
44 : /*************************************************************************/
45 :
46 2927 : std::wstring StringToWString(const std::string& utf8string)
47 : {
48 2927 : wchar_t* pszUTF16 = CPLRecodeToWChar( utf8string.c_str(), CPL_ENC_UTF8, CPL_ENC_UCS2);
49 2927 : std::wstring utf16string = pszUTF16;
50 2927 : CPLFree(pszUTF16);
51 0 : return utf16string;
52 : }
53 :
54 : /*************************************************************************/
55 : /* WStringToString() */
56 : /*************************************************************************/
57 :
58 12312 : std::string WStringToString(const std::wstring& utf16string)
59 : {
60 12312 : char* pszUTF8 = CPLRecodeFromWChar( utf16string.c_str(), CPL_ENC_UCS2, CPL_ENC_UTF8 );
61 12312 : std::string utf8string = pszUTF8;
62 12312 : CPLFree(pszUTF8);
63 0 : return utf8string;
64 : }
65 :
66 : /*************************************************************************/
67 : /* GDBErr() */
68 : /*************************************************************************/
69 :
70 5 : bool GDBErr(long int hr, std::string desc)
71 : {
72 5 : std::wstring fgdb_error_desc_w;
73 : fgdbError er;
74 5 : er = FileGDBAPI::ErrorInfo::GetErrorDescription(hr, fgdb_error_desc_w);
75 5 : if ( er == S_OK )
76 : {
77 5 : std::string fgdb_error_desc = WStringToString(fgdb_error_desc_w);
78 : CPLError( CE_Failure, CPLE_AppDefined,
79 5 : "Error: %s (%s)", desc.c_str(), fgdb_error_desc.c_str());
80 : }
81 : else
82 : {
83 : CPLError( CE_Failure, CPLE_AppDefined,
84 0 : "Error (%ld): %s", hr, desc.c_str());
85 : }
86 : // FIXME? EvenR: not sure if ClearErrors() is really necessary, but as it, it causes crashes in case of
87 : // repeated errors
88 : //FileGDBAPI::ErrorInfo::ClearErrors();
89 :
90 5 : return false;
91 : }
92 :
93 : /*************************************************************************/
94 : /* GDBDebug() */
95 : /*************************************************************************/
96 :
97 0 : bool GDBDebug(long int hr, std::string desc)
98 : {
99 0 : std::wstring fgdb_error_desc_w;
100 : fgdbError er;
101 0 : er = FileGDBAPI::ErrorInfo::GetErrorDescription(hr, fgdb_error_desc_w);
102 0 : if ( er == S_OK )
103 : {
104 0 : std::string fgdb_error_desc = WStringToString(fgdb_error_desc_w);
105 0 : CPLDebug("FGDB", "%s (%s)", desc.c_str(), fgdb_error_desc.c_str());
106 : }
107 : else
108 : {
109 0 : CPLDebug("FGDB", "%s", desc.c_str());
110 : }
111 : // FIXME? EvenR: not sure if ClearErrors() is really necessary, but as it, it causes crashes in case of
112 : // repeated errors
113 : //FileGDBAPI::ErrorInfo::ClearErrors();
114 :
115 0 : return false;
116 : }
117 :
118 : /*************************************************************************/
119 : /* GDBToOGRGeometry() */
120 : /*************************************************************************/
121 :
122 170 : bool GDBToOGRGeometry(string geoType, bool hasZ, OGRwkbGeometryType* pOut)
123 : {
124 170 : if (geoType == "esriGeometryPoint")
125 : {
126 46 : *pOut = hasZ? wkbPoint25D : wkbPoint;
127 : }
128 124 : else if (geoType == "esriGeometryMultipoint")
129 : {
130 20 : *pOut = hasZ? wkbMultiPoint25D : wkbMultiPoint;
131 : }
132 104 : else if (geoType == "esriGeometryLine")
133 : {
134 0 : *pOut = hasZ? wkbLineString25D : wkbLineString;
135 : }
136 104 : else if (geoType == "esriGeometryPolyline")
137 : {
138 40 : *pOut = hasZ? wkbMultiLineString25D : wkbMultiLineString;
139 : }
140 64 : else if (geoType == "esriGeometryPolygon" ||
141 : geoType == "esriGeometryMultiPatch")
142 : {
143 64 : *pOut = hasZ? wkbMultiPolygon25D : wkbMultiPolygon; // no mapping to single polygon
144 : }
145 : else
146 : {
147 : CPLError( CE_Failure, CPLE_AppDefined,
148 0 : "Cannot map esriGeometryType(%s) to OGRwkbGeometryType", geoType.c_str());
149 0 : return false;
150 : }
151 :
152 170 : return true;
153 : }
154 :
155 : /*************************************************************************/
156 : /* OGRGeometryToGDB() */
157 : /*************************************************************************/
158 :
159 36 : bool OGRGeometryToGDB(OGRwkbGeometryType ogrType, std::string *gdbType, bool *hasZ)
160 : {
161 36 : switch (ogrType)
162 : {
163 : /* 3D forms */
164 : case wkbPoint25D:
165 : {
166 1 : *gdbType = "esriGeometryPoint";
167 1 : *hasZ = true;
168 1 : break;
169 : }
170 :
171 : case wkbMultiPoint25D:
172 : {
173 1 : *gdbType = "esriGeometryMultipoint";
174 1 : *hasZ = true;
175 1 : break;
176 : }
177 :
178 : case wkbLineString25D:
179 : case wkbMultiLineString25D:
180 : {
181 2 : *gdbType = "esriGeometryPolyline";
182 2 : *hasZ = true;
183 2 : break;
184 : }
185 :
186 : case wkbPolygon25D:
187 : case wkbMultiPolygon25D:
188 : {
189 2 : *gdbType = "esriGeometryPolygon";
190 2 : *hasZ = true;
191 2 : break;
192 : }
193 :
194 : /* 2D forms */
195 : case wkbPoint:
196 : {
197 22 : *gdbType = "esriGeometryPoint";
198 22 : *hasZ = false;
199 22 : break;
200 : }
201 :
202 : case wkbMultiPoint:
203 : {
204 1 : *gdbType = "esriGeometryMultipoint";
205 1 : *hasZ = false;
206 1 : break;
207 : }
208 :
209 : case wkbLineString:
210 : case wkbMultiLineString:
211 : {
212 2 : *gdbType = "esriGeometryPolyline";
213 2 : *hasZ = false;
214 2 : break;
215 : }
216 :
217 : case wkbPolygon:
218 : case wkbMultiPolygon:
219 : {
220 5 : *gdbType = "esriGeometryPolygon";
221 5 : *hasZ = false;
222 5 : break;
223 : }
224 :
225 : default:
226 : {
227 : CPLError( CE_Failure, CPLE_AppDefined, "Cannot map OGRwkbGeometryType (%s) to ESRI type",
228 0 : OGRGeometryTypeToName(ogrType));
229 0 : return false;
230 : }
231 : }
232 36 : return true;
233 : }
234 :
235 : /*************************************************************************/
236 : /* GDBToOGRFieldType() */
237 : /*************************************************************************/
238 :
239 : // We could make this function far more robust by doing automatic coertion of types,
240 : // and/or skipping fields we do not know. But our purposes this works fine
241 568 : bool GDBToOGRFieldType(std::string gdbType, OGRFieldType* pOut)
242 : {
243 : /*
244 : ESRI types
245 : esriFieldTypeSmallInteger = 0,
246 : esriFieldTypeInteger = 1,
247 : esriFieldTypeSingle = 2,
248 : esriFieldTypeDouble = 3,
249 : esriFieldTypeString = 4,
250 : esriFieldTypeDate = 5,
251 : esriFieldTypeOID = 6,
252 : esriFieldTypeGeometry = 7,
253 : esriFieldTypeBlob = 8,
254 : esriFieldTypeRaster = 9,
255 : esriFieldTypeGUID = 10,
256 : esriFieldTypeGlobalID = 11,
257 : esriFieldTypeXML = 12
258 : */
259 :
260 : //OGR Types
261 :
262 : // Desc Name GDB->OGR Mapped By Us?
263 : /** Simple 32bit integer */// OFTInteger = 0, YES
264 : /** List of 32bit integers */// OFTIntegerList = 1, NO
265 : /** Double Precision floating point */// OFTReal = 2, YES
266 : /** List of doubles */// OFTRealList = 3, NO
267 : /** String of ASCII chars */// OFTString = 4, YES
268 : /** Array of strings */// OFTStringList = 5, NO
269 : /** deprecated */// OFTWideString = 6, NO
270 : /** deprecated */// OFTWideStringList = 7, NO
271 : /** Raw Binary data */// OFTBinary = 8, YES
272 : /** Date */// OFTDate = 9, NO
273 : /** Time */// OFTTime = 10, NO
274 : /** Date and Time */// OFTDateTime = 11 YES
275 :
276 568 : if (gdbType == "esriFieldTypeSmallInteger" ||
277 : gdbType == "esriFieldTypeInteger")
278 : {
279 274 : *pOut = OFTInteger;
280 274 : return true;
281 : }
282 294 : else if (gdbType == "esriFieldTypeSingle" ||
283 : gdbType == "esriFieldTypeDouble")
284 : {
285 145 : *pOut = OFTReal;
286 145 : return true;
287 : }
288 149 : else if (gdbType == "esriFieldTypeGUID" ||
289 : gdbType == "esriFieldTypeGlobalID" ||
290 : gdbType == "esriFieldTypeXML" ||
291 : gdbType == "esriFieldTypeString")
292 : {
293 145 : *pOut = OFTString;
294 145 : return true;
295 : }
296 4 : else if (gdbType == "esriFieldTypeDate")
297 : {
298 2 : *pOut = OFTDateTime;
299 2 : return true;
300 : }
301 2 : else if (gdbType == "esriFieldTypeBlob")
302 : {
303 2 : *pOut = OFTBinary;
304 2 : return true;
305 : }
306 : else
307 : {
308 : /* Intentionally fail at these
309 : esriFieldTypeOID
310 : esriFieldTypeGeometry
311 : esriFieldTypeRaster
312 : */
313 0 : CPLError( CE_Warning, CPLE_AppDefined, "%s", ("Cannot map field " + gdbType).c_str());
314 :
315 0 : return false;
316 : }
317 : }
318 :
319 : /*************************************************************************/
320 : /* OGRToGDBFieldType() */
321 : /*************************************************************************/
322 :
323 73 : bool OGRToGDBFieldType(OGRFieldType ogrType, std::string* gdbType)
324 : {
325 73 : switch(ogrType)
326 : {
327 : case OFTInteger:
328 : {
329 37 : *gdbType = "esriFieldTypeInteger";
330 37 : break;
331 : }
332 : case OFTReal:
333 : {
334 17 : *gdbType = "esriFieldTypeDouble";
335 17 : break;
336 : }
337 : case OFTString:
338 : {
339 19 : *gdbType = "esriFieldTypeString";
340 19 : break;
341 : }
342 : case OFTBinary:
343 : {
344 0 : *gdbType = "esriFieldTypeBlob";
345 0 : break;
346 : }
347 : case OFTDate:
348 : case OFTDateTime:
349 : {
350 0 : *gdbType = "esriFieldTypeDate";
351 0 : break;
352 : }
353 : default:
354 : {
355 : CPLError( CE_Warning, CPLE_AppDefined,
356 : "Cannot map OGR field type (%s)",
357 0 : OGR_GetFieldTypeName(ogrType) );
358 0 : return false;
359 : }
360 : }
361 :
362 73 : return true;
363 : }
364 :
365 : /*************************************************************************/
366 : /* GDBFieldTypeToWidthPrecision() */
367 : /*************************************************************************/
368 :
369 70 : bool GDBFieldTypeToWidthPrecision(std::string &gdbType, int *width, int *precision)
370 : {
371 70 : *precision = 0;
372 :
373 70 : if(gdbType == "esriFieldTypeSmallInteger" )
374 : {
375 0 : *width = 2;
376 : }
377 70 : else if(gdbType == "esriFieldTypeInteger" )
378 : {
379 37 : *width = 12;
380 : }
381 33 : else if(gdbType == "esriFieldTypeSingle" )
382 : {
383 0 : *width = 12;
384 0 : *precision = 5;
385 : }
386 33 : else if(gdbType == "esriFieldTypeDouble" )
387 : {
388 15 : *width = 24;
389 15 : *precision = 15;
390 : }
391 18 : else if(gdbType == "esriFieldTypeString" )
392 : {
393 18 : *width = 2147483647;
394 : }
395 0 : else if(gdbType == "esriFieldTypeDate" )
396 : {
397 0 : *width = 32;
398 : }
399 0 : else if(gdbType == "esriFieldTypeOID" )
400 : {
401 0 : *width = 15;
402 : }
403 : else
404 : {
405 : CPLError( CE_Warning, CPLE_AppDefined,
406 0 : "Cannot map ESRI field type (%s)", gdbType.c_str());
407 0 : return false;
408 : }
409 :
410 70 : return true;
411 : }
412 :
413 : /*************************************************************************/
414 : /* GDBFieldTypeToWidthPrecision() */
415 : /*************************************************************************/
416 :
417 801 : bool GDBGeometryToOGRGeometry(bool forceMulti, FileGDBAPI::ShapeBuffer* pGdbGeometry,
418 : OGRSpatialReference* pOGRSR, OGRGeometry** ppOutGeometry)
419 : {
420 :
421 801 : OGRGeometry* pOGRGeometry = NULL;
422 :
423 : OGRErr eErr = OGRCreateFromShapeBin( pGdbGeometry->shapeBuffer,
424 : &pOGRGeometry,
425 801 : pGdbGeometry->inUseLength);
426 :
427 : //OGRErr eErr = OGRGeometryFactory::createFromWkb(pGdbGeometry->shapeBuffer, pOGRSR, &pOGRGeometry, pGdbGeometry->inUseLength );
428 :
429 801 : if (eErr != OGRERR_NONE)
430 : {
431 0 : CPLError( CE_Failure, CPLE_AppDefined, "Failed attempting to import GDB WKB Geometry. OGRGeometryFactory err:%d", eErr);
432 0 : return false;
433 : }
434 :
435 801 : if( pOGRGeometry != NULL )
436 : {
437 : // force geometries to multi if requested
438 :
439 : // If it is a polygon, force to MultiPolygon since we always produce multipolygons
440 801 : if (wkbFlatten(pOGRGeometry->getGeometryType()) == wkbPolygon)
441 : {
442 332 : pOGRGeometry = OGRGeometryFactory::forceToMultiPolygon(pOGRGeometry);
443 : }
444 469 : else if (forceMulti)
445 : {
446 348 : if (wkbFlatten(pOGRGeometry->getGeometryType()) == wkbLineString)
447 : {
448 232 : pOGRGeometry = OGRGeometryFactory::forceToMultiLineString(pOGRGeometry);
449 : }
450 116 : else if (wkbFlatten(pOGRGeometry->getGeometryType()) == wkbPoint)
451 : {
452 0 : pOGRGeometry = OGRGeometryFactory::forceToMultiPoint(pOGRGeometry);
453 : }
454 : }
455 :
456 801 : if (pOGRGeometry)
457 801 : pOGRGeometry->assignSpatialReference( pOGRSR );
458 : }
459 :
460 :
461 801 : *ppOutGeometry = pOGRGeometry;
462 :
463 801 : return true;
464 : }
465 :
466 : /*************************************************************************/
467 : /* GDBToOGRSpatialReference() */
468 : /*************************************************************************/
469 :
470 0 : bool GDBToOGRSpatialReference(const string & wkt, OGRSpatialReference** ppSR)
471 : {
472 0 : if (wkt.size() <= 0)
473 : {
474 0 : CPLError( CE_Warning, CPLE_AppDefined, "ESRI Spatial Reference is NULL");
475 0 : return false;
476 : }
477 :
478 0 : *ppSR = new OGRSpatialReference(wkt.c_str());
479 :
480 0 : OGRErr result = (*ppSR)->morphFromESRI();
481 :
482 0 : if (result == OGRERR_NONE)
483 : {
484 0 : return true;
485 : }
486 : else
487 : {
488 0 : delete *ppSR;
489 0 : *ppSR = NULL;
490 :
491 : CPLError( CE_Failure, CPLE_AppDefined,
492 0 : "Failed morhping from ESRI Geometry: %s", wkt.c_str());
493 :
494 0 : return false;
495 : }
496 : }
497 :
498 : /*************************************************************************/
499 : /* FGDB_CPLAddXMLAttribute() */
500 : /*************************************************************************/
501 :
502 : /* Utility method for attributing nodes */
503 862 : void FGDB_CPLAddXMLAttribute(CPLXMLNode* node, const char* attrname, const char* attrvalue)
504 : {
505 862 : if ( !node ) return;
506 862 : CPLCreateXMLNode( CPLCreateXMLNode( node, CXT_Attribute, attrname ), CXT_Text, attrvalue );
507 : }
508 :
509 : /*************************************************************************/
510 : /* FGDBLaunderName() */
511 : /*************************************************************************/
512 :
513 111 : std::string FGDBLaunderName(const std::string name)
514 : {
515 111 : std::string newName = name;
516 :
517 111 : if ( newName[0]>='0' && newName[0]<='9' )
518 : {
519 2 : newName = "_" + newName;
520 : }
521 :
522 1444 : for(size_t i=0; i < newName.size(); i++)
523 : {
524 1333 : if ( !( newName[i] == '_' ||
525 : ( newName[i]>='0' && newName[i]<='9') ||
526 : ( newName[i]>='a' && newName[i]<='z') ||
527 : ( newName[i]>='A' && newName[i]<='Z') ))
528 : {
529 20 : newName[i] = '_';
530 : }
531 : }
532 :
533 0 : return newName;
534 : }
535 :
536 : /*************************************************************************/
537 : /* FGDBEscapeUnsupportedPrefixes() */
538 : /*************************************************************************/
539 :
540 38 : std::string FGDBEscapeUnsupportedPrefixes(const std::string className)
541 : {
542 38 : std::string newName = className;
543 : // From ESRI docs
544 : // Feature classes starting with these strings are unsupported.
545 : static const char* UNSUPPORTED_PREFIXES[] = {"sde_", "gdb_", "delta_", NULL};
546 :
547 149 : for (int i = 0; UNSUPPORTED_PREFIXES[i] != NULL; i++)
548 : {
549 112 : if (newName.find(UNSUPPORTED_PREFIXES[i]) == 0)
550 : {
551 1 : newName = "_" + newName;
552 1 : break;
553 : }
554 : }
555 :
556 0 : return newName;
557 : }
558 :
559 : /*************************************************************************/
560 : /* FGDBEscapeReservedKeywords() */
561 : /*************************************************************************/
562 :
563 111 : std::string FGDBEscapeReservedKeywords(const std::string name)
564 : {
565 111 : std::string newName = name;
566 111 : std::string upperName = name;
567 111 : std::transform(upperName.begin(), upperName.end(), upperName.begin(), ::toupper);
568 :
569 : // From ESRI docs
570 : static const char* RESERVED_WORDS[] = {FGDB_OID_NAME, "ADD", "ALTER", "AND", "AS", "ASC", "BETWEEN",
571 : "BY", "COLUMN", "CREATE", "DATE", "DELETE", "DESC",
572 : "DROP", "EXISTS", "FOR", "FROM", "IN", "INSERT", "INTO",
573 : "IS", "LIKE", "NOT", "NULL", "OR", "ORDER", "SELECT",
574 : "SET", "TABLE", "UPDATE", "VALUES", "WHERE", NULL};
575 :
576 : // Append an underscore to any FGDB reserved words used as field names
577 : // This is the same behaviour ArcCatalog follows.
578 3631 : for (int i = 0; RESERVED_WORDS[i] != NULL; i++)
579 : {
580 3522 : const char* w = RESERVED_WORDS[i];
581 3522 : if (upperName == w)
582 : {
583 2 : newName += '_';
584 2 : break;
585 : }
586 : }
587 :
588 111 : return newName;
589 2139 : }
|