1 /******************************************************************************
2  * $Id: sdedataset.cpp 27384 2014-05-24 12:28:12Z rouault $
3  *
4  * Project:  ESRI ArcSDE Raster reader
5  * Purpose:  Dataset implementaion for ESRI ArcSDE Rasters
6  * Author:   Howard Butler, hobu@hobu.net
7  *
8  * This work was sponsored by the Geological Survey of Canada, Natural
9  * Resources Canada. http://gsc.nrcan.gc.ca/
10  *
11  ******************************************************************************
12  * Copyright (c) 2007, Howard Butler <hobu@hobu.net>
13  *
14  * Permission is hereby granted, free of charge, to any person obtaining a
15  * copy of this software and associated documentation files (the "Software"),
16  * to deal in the Software without restriction, including without limitation
17  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18  * and/or sell copies of the Software, and to permit persons to whom the
19  * Software is furnished to do so, subject to the following conditions:
20  *
21  * The above copyright notice and this permission notice shall be included
22  * in all copies or substantial portions of the Software.
23  *
24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30  * DEALINGS IN THE SOFTWARE.
31  ****************************************************************************/
32 
33 #include "sdedataset.h"
34 
35 
36 /************************************************************************/
37 /*                          GetRastercount()                            */
38 /************************************************************************/
39 
GetRasterCount(void)40 int SDEDataset::GetRasterCount( void )
41 
42 {
43     return nBands;
44 }
45 
46 /************************************************************************/
47 /*                          GetRasterXSize()                            */
48 /************************************************************************/
49 
GetRasterXSize(void)50 int SDEDataset::GetRasterXSize( void )
51 
52 {
53     return nRasterXSize;
54 }
55 
56 /************************************************************************/
57 /*                          GetRasterYSize()                            */
58 /************************************************************************/
59 
GetRasterYSize(void)60 int SDEDataset::GetRasterYSize( void )
61 
62 {
63     return nRasterYSize;
64 }
65 
66 
67 /************************************************************************/
68 /*                          ComputeRasterInfo()                         */
69 /************************************************************************/
ComputeRasterInfo()70 CPLErr SDEDataset::ComputeRasterInfo() {
71     long nSDEErr;
72     SE_RASTERINFO raster;
73 
74     nSDEErr = SE_rasterinfo_create(&raster);
75     if( nSDEErr != SE_SUCCESS )
76     {
77         IssueSDEError( nSDEErr, "SE_rasterinfo_create" );
78         return CE_Fatal;
79     }
80 
81     LONG nRasterColumnId = 0;
82 
83     nSDEErr = SE_rascolinfo_get_id( hRasterColumn,
84                                     &nRasterColumnId);
85     if( nSDEErr != SE_SUCCESS )
86     {
87         IssueSDEError( nSDEErr, "SE_rascolinfo_get_id" );
88         return CE_Fatal;
89     }
90 
91     nSDEErr = SE_raster_get_info_by_id( hConnection,
92                                         nRasterColumnId,
93                                         1,
94                                         raster);
95     if( nSDEErr != SE_SUCCESS )
96     {
97         IssueSDEError( nSDEErr, "SE_rascolinfo_get_id" );
98         return CE_Fatal;
99     }
100 
101     LONG nBandsRet;
102     nSDEErr = SE_raster_get_bands(  hConnection,
103                                     raster,
104                                     &paohSDERasterBands,
105                                     &nBandsRet);
106     if( nSDEErr != SE_SUCCESS )
107     {
108         IssueSDEError( nSDEErr, "SE_raster_get_bands" );
109         return CE_Fatal;
110     }
111 
112     nBands = nBandsRet;
113 
114     SE_RASBANDINFO band;
115 
116     // grab our other stuff from the first band and hope for the best
117     band = paohSDERasterBands[0];
118 
119     LONG nXSRet, nYSRet;
120     nSDEErr = SE_rasbandinfo_get_band_size( band, &nXSRet, &nYSRet );
121     if( nSDEErr != SE_SUCCESS )
122     {
123         IssueSDEError( nSDEErr, "SE_rasbandinfo_get_band_size" );
124         return CE_Fatal;
125     }
126 
127     nRasterXSize = nXSRet;
128     nRasterYSize = nYSRet;
129 
130     SE_ENVELOPE extent;
131     nSDEErr = SE_rasbandinfo_get_extent(band, &extent);
132     if( nSDEErr != SE_SUCCESS )
133     {
134         IssueSDEError( nSDEErr, "SE_rasbandinfo_get_extent" );
135         return CE_Fatal;
136     }
137     dfMinX = extent.minx;
138     dfMinY = extent.miny;
139     dfMaxX = extent.maxx;
140     dfMaxY = extent.maxy;
141 
142     CPLDebug("SDERASTER", "minx: %.5f, miny: %.5f, maxx: %.5f, maxy: %.5f", dfMinX, dfMinY, dfMaxX, dfMaxY);
143 
144     // x0 roughly corresponds to dfMinX
145     // y0 roughly corresponds to dfMaxY
146     // They can be different than the extent parameters because
147     // SDE uses offsets.  The following info is from Duarte Carreira
148     // in relation to bug #2063 http://trac.osgeo.org/gdal/ticket/2063 :
149 
150     // Depending on how the data was loaded, the pixel width
151     // or pixel height may include a pixel offset from the nearest
152     // tile boundary. An offset will be indicated by aplus sign
153     // "+" followed by a value. The value indicates the number
154     // of pixels the nearest tile boundary is to the left of
155     // the image for the x dimension or the number of
156     // pixels the nearest tile boundary is above the image for
157     // the y dimension. The offset information is only useful
158     // for advanced application developers who need to know
159     // where the image begins in relation to the underlying tile structure
160 
161     LFLOAT x0, y0;
162     nSDEErr = SE_rasbandinfo_get_tile_origin(band, &x0, &y0);
163     if( nSDEErr != SE_SUCCESS )
164     {
165         IssueSDEError( nSDEErr, "SE_rasbandinfo_get_tile_origin" );
166         return CE_Fatal;
167     }
168     CPLDebug("SDERASTER", "Tile origin: %.5f, %.5f", x0, y0);
169 
170     // we also need to adjust dfMaxX and dfMinY otherwise the cell size will change
171     dfMaxX = (x0-dfMinX) + dfMaxX;
172     dfMinY = (y0-dfMaxY) + dfMinY;
173 
174     // adjust the bbox based on the tile origin.
175     dfMinX = MIN(x0, dfMinX);
176     dfMaxY = MAX(y0, dfMaxY);
177 
178     nSDEErr = SE_rasterattr_create(&hAttributes, false);
179     if( nSDEErr != SE_SUCCESS )
180     {
181         IssueSDEError( nSDEErr, "SE_rasterattr_create" );
182         return CE_Fatal;
183     }
184 
185     // Grab the pointer for our member variable
186 
187     nSDEErr = SE_stream_create(hConnection, &hStream);
188     if( nSDEErr != SE_SUCCESS )
189     {
190         IssueSDEError( nSDEErr, "SE_stream_create" );
191         return CE_Fatal;
192     }
193 
194 
195     for (int i=0; i < nBands; i++) {
196         SetBand( i+1, new SDERasterBand( this, i+1, -1, &(paohSDERasterBands[i]) ));
197     }
198 
199     GDALRasterBand* b = GetRasterBand(1);
200 
201     eDataType = b->GetRasterDataType();
202 
203     SE_rasterinfo_free(raster);
204 
205     return CE_None;
206 }
207 
208 
209 /************************************************************************/
210 /*                          GetGeoTransform()                           */
211 /************************************************************************/
212 
GetGeoTransform(double * padfTransform)213 CPLErr SDEDataset::GetGeoTransform( double * padfTransform )
214 
215 {
216 
217     if (dfMinX == 0.0 && dfMinY == 0.0 && dfMaxX == 0.0 && dfMaxY == 0.0)
218         return CE_Fatal;
219 
220     padfTransform[0] = dfMinX - 0.5*(dfMaxX - dfMinX) / (GetRasterXSize()-1);
221     padfTransform[3] = dfMaxY + 0.5*(dfMaxY - dfMinY) / (GetRasterYSize()-1);
222 
223     padfTransform[1] = (dfMaxX - dfMinX) / (GetRasterXSize()-1);
224     padfTransform[2] = 0.0;
225 
226     padfTransform[4] = 0.0;
227     padfTransform[5] = -1 * (dfMaxY - dfMinY) / (GetRasterYSize()-1);
228 
229     return CE_None;
230 }
231 
232 /************************************************************************/
233 /*                          GetProjectionRef()                          */
234 /************************************************************************/
235 
GetProjectionRef()236 const char *SDEDataset::GetProjectionRef()
237 
238 {
239     long nSDEErr;
240     SE_COORDREF coordref;
241     nSDEErr = SE_coordref_create(&coordref);
242 
243     if( nSDEErr != SE_SUCCESS )
244     {
245         IssueSDEError( nSDEErr, "SE_coordref_create" );
246         return FALSE;
247     }
248 
249     if (!hRasterColumn){
250         CPLError ( CE_Failure, CPLE_AppDefined,
251                    "Raster Column not defined");
252         return ("");
253     }
254 
255     nSDEErr = SE_rascolinfo_get_coordref(hRasterColumn, coordref);
256 
257     if (nSDEErr == SE_NO_COORDREF) {
258         return ("");
259     }
260 
261     if( nSDEErr != SE_SUCCESS )
262     {
263         IssueSDEError( nSDEErr, "SE_rascolinfo_get_coordref" );
264     }
265 
266     char szWKT[SE_MAX_SPATIALREF_SRTEXT_LEN];
267     nSDEErr = SE_coordref_get_description(coordref, szWKT);
268     if (nSDEErr != SE_SUCCESS )
269     {
270         IssueSDEError( nSDEErr, "SE_coordref_get_description");
271     }
272     SE_coordref_free(coordref);
273 
274     OGRSpatialReference *poSRS;
275     CPLDebug ("SDERASTER", "SDE says the coordinate system is: %s'", szWKT);
276     poSRS = new OGRSpatialReference(szWKT);
277     poSRS->morphFromESRI();
278 
279     poSRS->exportToWkt(&pszWKT);
280     poSRS->Release();
281 
282     return pszWKT;
283 }
284 
285 /************************************************************************/
286 /*                                SDEDataset()                          */
287 /************************************************************************/
288 
SDEDataset()289 SDEDataset::SDEDataset(  )
290 
291 {
292     hConnection         = NULL;
293     nSubDataCount       = 0;
294     pszLayerName        = NULL;
295     hAttributes         = NULL;
296     pszColumnName       = NULL;
297     paohSDERasterColumns  = NULL;
298     paohSDERasterBands  = NULL;
299     hStream             = NULL;
300     hRasterColumn       = NULL;
301     pszWKT		= NULL;
302     pszLayerName 	= NULL;
303     pszColumnName 	= NULL;
304     nBands              = 0;
305     nRasterXSize        = 0;
306     nRasterYSize        = 0;
307 
308     dfMinX              = 0.0;
309     dfMinY              = 0.0;
310     dfMaxX              = 0.0;
311     dfMaxY              = 0.0;
312     SE_rascolinfo_create(&hRasterColumn);
313 
314 }
315 
316 
317 
318 /************************************************************************/
319 /*                            ~SDEDataset()                             */
320 /************************************************************************/
321 
~SDEDataset()322 SDEDataset::~SDEDataset()
323 
324 {
325 
326     if (paohSDERasterBands)
327         SE_rasterband_free_info_list(nBands, paohSDERasterBands);
328 
329     if (hRasterColumn)
330         SE_rascolinfo_free(hRasterColumn);
331 
332     if (hStream)
333         SE_stream_free(hStream);
334 
335     if (hAttributes)
336         SE_rasterattr_free(hAttributes);
337 
338     if (hConnection)
339         SE_connection_free(hConnection);
340 
341     if (pszWKT)
342         CPLFree(pszWKT);
343 
344     if (pszLayerName)
345         CPLFree(pszLayerName);
346 
347     if (pszColumnName)
348         CPLFree(pszColumnName);
349 }
350 
351 
352 /************************************************************************/
353 /*                                Open()                                */
354 /************************************************************************/
355 
Open(GDALOpenInfo * poOpenInfo)356 GDALDataset *SDEDataset::Open( GDALOpenInfo * poOpenInfo )
357 
358 {
359 
360 /* -------------------------------------------------------------------- */
361 /*      If we aren't prefixed with SDE: then ignore this datasource.    */
362 /* -------------------------------------------------------------------- */
363     if( !EQUALN(poOpenInfo->pszFilename,"SDE:",4) )
364         return NULL;
365 
366 /* -------------------------------------------------------------------- */
367 /*      Parse arguments on comma.  We expect (layer is optional):       */
368 /*        SDE:server,instance,database,username,password,layer          */
369 /* -------------------------------------------------------------------- */
370     char **papszTokens = CSLTokenizeStringComplex(  poOpenInfo->pszFilename+4,
371                                                     ",",
372                                                     TRUE,
373                                                     TRUE );
374     CPLDebug(   "SDERASTER", "Open(\"%s\") revealed %d tokens.",
375                 poOpenInfo->pszFilename,
376                 CSLCount( papszTokens ) );
377 
378 
379     if( CSLCount( papszTokens ) < 5 || CSLCount( papszTokens ) > 7 )
380     {
381         CPLError( CE_Failure, CPLE_OpenFailed,
382                   "SDE connect string had wrong number of arguments.\n"
383                   "Expected 'SDE:server,instance,database,username,password,layer'\n"
384                   "The layer name value is optional.\n"
385                   "Got '%s'",
386                   poOpenInfo->pszFilename );
387         return NULL;
388     }
389 
390 /* -------------------------------------------------------------------- */
391 /*      Create a corresponding GDALDataset.                             */
392 /* -------------------------------------------------------------------- */
393 
394     SDEDataset *poDS;
395 
396     poDS = new SDEDataset();
397 /* -------------------------------------------------------------------- */
398 /*      Try to establish connection.                                    */
399 /* -------------------------------------------------------------------- */
400     int         nSDEErr;
401     SE_ERROR    hSDEErrorInfo;
402     nSDEErr = SE_connection_create( papszTokens[0],
403                                     papszTokens[1],
404                                     papszTokens[2],
405                                     papszTokens[3],
406                                     papszTokens[4],
407                                     &(hSDEErrorInfo),
408                                     &(poDS->hConnection) );
409 
410     if( nSDEErr != SE_SUCCESS )
411     {
412         IssueSDEError( nSDEErr, "SE_connection_create" );
413         return NULL;
414     }
415 
416 
417 /* -------------------------------------------------------------------- */
418 /*      Set unprotected concurrency policy, suitable for single         */
419 /*      threaded access.                                                */
420 /* -------------------------------------------------------------------- */
421     nSDEErr = SE_connection_set_concurrency( poDS->hConnection,
422                                              SE_UNPROTECTED_POLICY);
423 
424     if( nSDEErr != SE_SUCCESS) {
425         IssueSDEError( nSDEErr, NULL );
426         return NULL;
427     }
428 
429 /* -------------------------------------------------------------------- */
430 /*      If we were given a layer name, use that directly, otherwise     */
431 /*      query for subdatasets.                                          */
432 /* -------------------------------------------------------------------- */
433 
434     // Get the RASTER column name if it was set
435     if (CSLCount (papszTokens) == 7) {
436         poDS->pszColumnName = CPLStrdup( papszTokens[6] );
437     }
438     else {
439         poDS->pszColumnName = CPLStrdup( "RASTER" );
440     }
441 
442     CPLDebug ("SDERASTER", "SDE Column name is '%s'", poDS->pszColumnName);
443 
444     if (CSLCount( papszTokens ) >= 6 ) {
445 
446         poDS->pszLayerName = CPLStrdup( papszTokens[5] );
447 
448 
449         nSDEErr =   SE_rascolinfo_create  (&(poDS->hRasterColumn));
450         if( nSDEErr != SE_SUCCESS )
451         {
452             IssueSDEError( nSDEErr, "SE_rastercolumn_create" );
453             return NULL;
454         }
455         CPLDebug( "SDERASTER", "'%s' raster layer specified... "\
456                                "using it directly with '%s' as the raster column name.",
457                   poDS->pszLayerName,
458                   poDS->pszColumnName);
459         nSDEErr = SE_rastercolumn_get_info_by_name( poDS->hConnection,
460                                                     poDS->pszLayerName,
461                                                     poDS->pszColumnName,
462                                                     poDS->hRasterColumn);
463         if( nSDEErr != SE_SUCCESS )
464         {
465             IssueSDEError( nSDEErr, "SE_rastercolumn_get_info_by_name" );
466             return NULL;
467         }
468         poDS->ComputeRasterInfo();
469 
470 
471 
472     } else {
473 
474         nSDEErr = SE_rastercolumn_get_info_list(poDS->hConnection,
475                                                 &(poDS->paohSDERasterColumns),
476                                                 &(poDS->nSubDataCount));
477         if( nSDEErr != SE_SUCCESS )
478         {
479             IssueSDEError( nSDEErr, "SE_rascolinfo_get_info_list" );
480             return NULL;
481         }
482 
483         CPLDebug( "SDERASTER", "No layername specified, %d subdatasets available.",
484                   poDS->nSubDataCount);
485 
486 
487         for (int i = 0; i < poDS->nSubDataCount; i++) {
488 
489               char         szTableName[SE_QUALIFIED_TABLE_NAME+1];
490               char         szColumnName[SE_MAX_COLUMN_LEN+1];
491             nSDEErr = SE_rascolinfo_get_raster_column (poDS->paohSDERasterColumns[i],
492                                                        szTableName,
493                                                        szColumnName);
494             CPLDebug(   "SDERASTER",
495                         "Layer '%s' with column '%s' found.",
496                         szTableName,
497                         szColumnName);
498 
499             if( nSDEErr != SE_SUCCESS )
500             {
501                 IssueSDEError( nSDEErr, "SE_rascolinfo_get_raster_column" );
502                 return NULL;
503             }
504         }
505 
506     return NULL;
507     }
508     CSLDestroy( papszTokens);
509     return( poDS );
510 }
511 
512 /************************************************************************/
513 /*                          GDALRegister_SDE()                          */
514 /************************************************************************/
515 
GDALRegister_SDE()516 void GDALRegister_SDE()
517 
518 {
519     GDALDriver  *poDriver;
520 
521     if (! GDAL_CHECK_VERSION("SDE driver"))
522         return;
523 
524     if( GDALGetDriverByName( "SDE" ) == NULL )
525     {
526         poDriver = new GDALDriver();
527 
528         poDriver->SetDescription( "SDE" );
529         poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
530         poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
531                                    "ESRI ArcSDE" );
532         poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
533                                    "frmt_various.html#SDE" );
534 
535         poDriver->pfnOpen = SDEDataset::Open;
536 
537         GetGDALDriverManager()->RegisterDriver( poDriver );
538     }
539 }
540