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