1 : /******************************************************************************
2 : * $Id: wcsdataset.cpp 22618 2011-06-29 20:02:57Z rouault $
3 : *
4 : * Project: WCS Client Driver
5 : * Purpose: Implementation of Dataset and RasterBand classes for WCS.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2006, Frank Warmerdam
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "gdal_pam.h"
31 : #include "cpl_string.h"
32 : #include "cpl_minixml.h"
33 : #include "cpl_http.h"
34 : #include "ogr_spatialref.h"
35 :
36 : CPL_CVSID("$Id: wcsdataset.cpp 22618 2011-06-29 20:02:57Z rouault $");
37 :
38 : /************************************************************************/
39 : /* ==================================================================== */
40 : /* WCSDataset */
41 : /* ==================================================================== */
42 : /************************************************************************/
43 :
44 : class WCSRasterBand;
45 :
46 : class CPL_DLL WCSDataset : public GDALPamDataset
47 : {
48 : friend class WCSRasterBand;
49 :
50 : int bServiceDirty;
51 : CPLXMLNode *psService;
52 :
53 : char *apszCoverageOfferingMD[2];
54 :
55 : char **papszSDSModifiers;
56 :
57 : int nVersion; // eg 100 for 1.0.0, 110 for 1.1.0
58 :
59 : CPLString osCRS;
60 :
61 : char *pszProjection;
62 : double adfGeoTransform[6];
63 :
64 : CPLString osBandIdentifier;
65 :
66 : CPLString osDefaultTime;
67 : std::vector<CPLString> aosTimePositions;
68 :
69 : int TestUseBlockIO( int, int, int, int, int, int );
70 : CPLErr DirectRasterIO( GDALRWFlag, int, int, int, int,
71 : void *, int, int, GDALDataType,
72 : int, int *, int, int, int );
73 : CPLErr GetCoverage( int nXOff, int nYOff, int nXSize, int nYSize,
74 : int nBufXSize, int nBufYSize,
75 : int nBandCount, int *panBandList,
76 : CPLHTTPResult **ppsResult );
77 :
78 : virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int,
79 : void *, int, int, GDALDataType,
80 : int, int *, int, int, int );
81 :
82 : int DescribeCoverage();
83 : int ExtractGridInfo100();
84 : int ExtractGridInfo();
85 : int EstablishRasterDetails();
86 :
87 : int ProcessError( CPLHTTPResult *psResult );
88 : GDALDataset *GDALOpenResult( CPLHTTPResult *psResult );
89 : void FlushMemoryResult();
90 : CPLString osResultFilename;
91 : GByte *pabySavedDataBuffer;
92 :
93 : char **papszHttpOptions;
94 :
95 : int nMaxCols;
96 : int nMaxRows;
97 :
98 : public:
99 : WCSDataset();
100 : ~WCSDataset();
101 :
102 : static GDALDataset *Open( GDALOpenInfo * );
103 : static int Identify( GDALOpenInfo * );
104 :
105 : virtual CPLErr GetGeoTransform( double * );
106 : virtual const char *GetProjectionRef(void);
107 : virtual char **GetFileList(void);
108 :
109 : virtual char **GetMetadata( const char *pszDomain );
110 : };
111 :
112 : /************************************************************************/
113 : /* ==================================================================== */
114 : /* WCSRasterBand */
115 : /* ==================================================================== */
116 : /************************************************************************/
117 :
118 : class WCSRasterBand : public GDALPamRasterBand
119 : {
120 : friend class WCSDataset;
121 :
122 : int iOverview;
123 : int nResFactor;
124 :
125 : WCSDataset *poODS;
126 :
127 : int nOverviewCount;
128 : WCSRasterBand **papoOverviews;
129 :
130 : virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int,
131 : void *, int, int, GDALDataType,
132 : int, int );
133 :
134 : public:
135 :
136 : WCSRasterBand( WCSDataset *, int nBand, int iOverview );
137 : ~WCSRasterBand();
138 :
139 : virtual double GetNoDataValue( int *pbSuccess = NULL );
140 :
141 : virtual int GetOverviewCount();
142 : virtual GDALRasterBand *GetOverview(int);
143 :
144 : virtual CPLErr IReadBlock( int, int, void * );
145 : };
146 :
147 : /************************************************************************/
148 : /* WCSRasterBand() */
149 : /************************************************************************/
150 :
151 12 : WCSRasterBand::WCSRasterBand( WCSDataset *poDS, int nBand, int iOverview )
152 :
153 : {
154 12 : poODS = poDS;
155 12 : this->nBand = nBand;
156 :
157 : eDataType = GDALGetDataTypeByName(
158 12 : CPLGetXMLValue( poDS->psService, "BandType", "Byte" ) );
159 :
160 : /* -------------------------------------------------------------------- */
161 : /* Establish resolution reduction for this overview level. */
162 : /* -------------------------------------------------------------------- */
163 12 : this->iOverview = iOverview;
164 12 : nResFactor = 1 << (iOverview+1); // iOverview == -1 is base layer
165 :
166 : /* -------------------------------------------------------------------- */
167 : /* Establish block size. */
168 : /* -------------------------------------------------------------------- */
169 12 : nRasterXSize = poDS->GetRasterXSize() / nResFactor;
170 12 : nRasterYSize = poDS->GetRasterYSize() / nResFactor;
171 :
172 12 : nBlockXSize = atoi(CPLGetXMLValue( poDS->psService, "BlockXSize", "0" ) );
173 12 : nBlockYSize = atoi(CPLGetXMLValue( poDS->psService, "BlockYSize", "0" ) );
174 :
175 12 : if( nBlockXSize < 1 )
176 : {
177 12 : if( nRasterXSize > 1800 )
178 0 : nBlockXSize = 1024;
179 : else
180 12 : nBlockXSize = nRasterXSize;
181 : }
182 :
183 12 : if( nBlockYSize < 1 )
184 : {
185 12 : if( nRasterYSize > 900 )
186 0 : nBlockYSize = 512;
187 : else
188 12 : nBlockYSize = nRasterYSize;
189 : }
190 :
191 : /* -------------------------------------------------------------------- */
192 : /* If this is the base layer, create the overview layers. */
193 : /* -------------------------------------------------------------------- */
194 12 : if( iOverview == -1 )
195 : {
196 : int i;
197 :
198 : nOverviewCount = atoi(CPLGetXMLValue(poODS->psService,"OverviewCount",
199 6 : "-1"));
200 6 : if( nOverviewCount < 0 )
201 : {
202 6 : for( nOverviewCount = 0;
203 : (MAX(nRasterXSize,nRasterYSize) / (1 << nOverviewCount)) > 900;
204 : nOverviewCount++ ) {}
205 : }
206 0 : else if( nOverviewCount > 30 )
207 : {
208 : /* There's no reason to have more than 30 overviews, because */
209 : /* 2^(30+1) overflows a int32 */
210 0 : nOverviewCount = 30;
211 : }
212 :
213 : papoOverviews = (WCSRasterBand **)
214 6 : CPLCalloc( nOverviewCount, sizeof(void*) );
215 :
216 24 : for( i = 0; i < nOverviewCount; i++ )
217 6 : papoOverviews[i] = new WCSRasterBand( poODS, nBand, i );
218 : }
219 : else
220 : {
221 6 : nOverviewCount = 0;
222 6 : papoOverviews = NULL;
223 : }
224 12 : }
225 :
226 : /************************************************************************/
227 : /* ~WCSRasterBand() */
228 : /************************************************************************/
229 :
230 12 : WCSRasterBand::~WCSRasterBand()
231 :
232 : {
233 12 : FlushCache();
234 :
235 12 : if( nOverviewCount > 0 )
236 : {
237 : int i;
238 :
239 12 : for( i = 0; i < nOverviewCount; i++ )
240 6 : delete papoOverviews[i];
241 :
242 6 : CPLFree( papoOverviews );
243 : }
244 12 : }
245 :
246 : /************************************************************************/
247 : /* IReadBlock() */
248 : /************************************************************************/
249 :
250 1 : CPLErr WCSRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
251 : void * pImage )
252 :
253 : {
254 : CPLErr eErr;
255 1 : CPLHTTPResult *psResult = NULL;
256 :
257 : eErr = poODS->GetCoverage( nBlockXOff * nBlockXSize * nResFactor,
258 : nBlockYOff * nBlockYSize * nResFactor,
259 : nBlockXSize * nResFactor,
260 : nBlockYSize * nResFactor,
261 : nBlockXSize, nBlockYSize,
262 1 : 1, &nBand, &psResult );
263 1 : if( eErr != CE_None )
264 0 : return eErr;
265 :
266 : /* -------------------------------------------------------------------- */
267 : /* Try and open result as a dataseat. */
268 : /* -------------------------------------------------------------------- */
269 1 : GDALDataset *poTileDS = poODS->GDALOpenResult( psResult );
270 :
271 1 : if( poTileDS == NULL )
272 0 : return CE_Failure;
273 :
274 : /* -------------------------------------------------------------------- */
275 : /* Verify configuration. */
276 : /* -------------------------------------------------------------------- */
277 1 : if( poTileDS->GetRasterXSize() != nBlockXSize
278 : || poTileDS->GetRasterYSize() != nBlockYSize )
279 : {
280 : CPLDebug( "WCS", "Got size=%dx%d instead of %dx%d.",
281 : poTileDS->GetRasterXSize(), poTileDS->GetRasterYSize(),
282 0 : nBlockXSize, nBlockYSize );
283 :
284 : CPLError( CE_Failure, CPLE_AppDefined,
285 : "Returned tile does not match expected configuration.\n"
286 : "Got %dx%d instead of %dx%d.",
287 : poTileDS->GetRasterXSize(), poTileDS->GetRasterYSize(),
288 0 : nBlockXSize, nBlockYSize );
289 0 : delete poTileDS;
290 0 : return CE_Failure;
291 : }
292 :
293 1 : if( (strlen(poODS->osBandIdentifier) && poTileDS->GetRasterCount() != 1)
294 : || (!strlen(poODS->osBandIdentifier)
295 : && poTileDS->GetRasterCount() != poODS->GetRasterCount()) )
296 : {
297 : CPLError( CE_Failure, CPLE_AppDefined,
298 0 : "Returned tile does not match expected band configuration.");
299 0 : delete poTileDS;
300 0 : return CE_Failure;
301 : }
302 :
303 : /* -------------------------------------------------------------------- */
304 : /* Process all bands of memory result, copying into pBuffer, or */
305 : /* pushing into cache for other bands. */
306 : /* -------------------------------------------------------------------- */
307 : int iBand;
308 1 : eErr = CE_None;
309 :
310 4 : for( iBand = 0;
311 : iBand < poTileDS->GetRasterCount() && eErr == CE_None;
312 : iBand++ )
313 : {
314 3 : GDALRasterBand *poTileBand = poTileDS->GetRasterBand( iBand+1 );
315 :
316 3 : if( iBand+1 == GetBand() || strlen(poODS->osBandIdentifier) )
317 : {
318 : eErr = poTileBand->RasterIO( GF_Read,
319 : 0, 0, nBlockXSize, nBlockYSize,
320 : pImage, nBlockXSize, nBlockYSize,
321 1 : eDataType, 0, 0 );
322 : }
323 : else
324 : {
325 2 : GDALRasterBand *poTargBand = poODS->GetRasterBand( iBand+1 );
326 :
327 2 : if( iOverview != -1 )
328 0 : poTargBand = poTargBand->GetOverview( iOverview );
329 :
330 : GDALRasterBlock *poBlock = poTargBand->GetLockedBlockRef(
331 2 : nBlockXOff, nBlockYOff, TRUE );
332 :
333 2 : if( poBlock != NULL )
334 : {
335 : eErr = poTileBand->RasterIO( GF_Read,
336 : 0, 0, nBlockXSize, nBlockYSize,
337 : poBlock->GetDataRef(),
338 : nBlockXSize, nBlockYSize,
339 2 : eDataType, 0, 0 );
340 2 : poBlock->DropLock();
341 : }
342 : else
343 0 : eErr = CE_Failure;
344 : }
345 : }
346 :
347 : /* -------------------------------------------------------------------- */
348 : /* Cleanup */
349 : /* -------------------------------------------------------------------- */
350 1 : delete poTileDS;
351 :
352 1 : poODS->FlushMemoryResult();
353 :
354 1 : return eErr;
355 : }
356 :
357 : /************************************************************************/
358 : /* IRasterIO() */
359 : /************************************************************************/
360 :
361 598 : CPLErr WCSRasterBand::IRasterIO( GDALRWFlag eRWFlag,
362 : int nXOff, int nYOff, int nXSize, int nYSize,
363 : void * pData, int nBufXSize, int nBufYSize,
364 : GDALDataType eBufType,
365 : int nPixelSpace, int nLineSpace )
366 :
367 : {
368 598 : if( (poODS->nMaxCols > 0 && poODS->nMaxCols < nBufXSize)
369 : || (poODS->nMaxRows > 0 && poODS->nMaxRows < nBufYSize) )
370 0 : return CE_Failure;
371 :
372 598 : if( poODS->TestUseBlockIO( nXOff, nYOff, nXSize, nYSize,
373 : nBufXSize,nBufYSize ) )
374 : return GDALPamRasterBand::IRasterIO(
375 : eRWFlag, nXOff, nYOff, nXSize, nYSize,
376 : pData, nBufXSize, nBufYSize, eBufType,
377 598 : nPixelSpace, nLineSpace );
378 : else
379 : return poODS->DirectRasterIO(
380 : eRWFlag,
381 : nXOff * nResFactor, nYOff * nResFactor,
382 : nXSize * nResFactor, nYSize * nResFactor,
383 : pData, nBufXSize, nBufYSize, eBufType,
384 0 : 1, &nBand, nPixelSpace, nLineSpace, 0 );
385 : }
386 :
387 : /************************************************************************/
388 : /* GetNoDataValue() */
389 : /************************************************************************/
390 :
391 0 : double WCSRasterBand::GetNoDataValue( int *pbSuccess )
392 :
393 : {
394 0 : const char *pszSV = CPLGetXMLValue( poODS->psService, "NoDataValue", NULL);
395 :
396 0 : if( pszSV == NULL )
397 0 : return GDALPamRasterBand::GetNoDataValue( pbSuccess );
398 : else
399 : {
400 0 : if( pbSuccess )
401 0 : *pbSuccess = TRUE;
402 0 : return atof(pszSV);
403 : }
404 : }
405 :
406 : /************************************************************************/
407 : /* GetOverviewCount() */
408 : /************************************************************************/
409 :
410 1 : int WCSRasterBand::GetOverviewCount()
411 :
412 : {
413 1 : return nOverviewCount;
414 : }
415 :
416 : /************************************************************************/
417 : /* GetOverview() */
418 : /************************************************************************/
419 :
420 0 : GDALRasterBand *WCSRasterBand::GetOverview( int iOverview )
421 :
422 : {
423 0 : if( iOverview < 0 || iOverview >= nOverviewCount )
424 0 : return NULL;
425 : else
426 0 : return papoOverviews[iOverview];
427 : }
428 :
429 : /************************************************************************/
430 : /* ==================================================================== */
431 : /* WCSDataset */
432 : /* ==================================================================== */
433 : /************************************************************************/
434 :
435 :
436 : /************************************************************************/
437 : /* WCSDataset() */
438 : /************************************************************************/
439 :
440 2 : WCSDataset::WCSDataset()
441 :
442 : {
443 2 : psService = NULL;
444 2 : bServiceDirty = FALSE;
445 2 : pszProjection = NULL;
446 :
447 2 : adfGeoTransform[0] = 0.0;
448 2 : adfGeoTransform[1] = 1.0;
449 2 : adfGeoTransform[2] = 0.0;
450 2 : adfGeoTransform[3] = 0.0;
451 2 : adfGeoTransform[4] = 0.0;
452 2 : adfGeoTransform[5] = 1.0;
453 :
454 2 : pabySavedDataBuffer = NULL;
455 2 : papszHttpOptions = NULL;
456 :
457 2 : nMaxCols = -1;
458 2 : nMaxRows = -1;
459 :
460 2 : apszCoverageOfferingMD[0] = NULL;
461 2 : apszCoverageOfferingMD[1] = NULL;
462 :
463 2 : papszSDSModifiers = NULL;
464 2 : }
465 :
466 : /************************************************************************/
467 : /* ~WCSDataset() */
468 : /************************************************************************/
469 :
470 2 : WCSDataset::~WCSDataset()
471 :
472 : {
473 : // perhaps this should be moved into a FlushCache() method.
474 2 : if( bServiceDirty && !EQUALN(GetDescription(),"<WCS_GDAL>",10) )
475 : {
476 1 : CPLSerializeXMLTreeToFile( psService, GetDescription() );
477 1 : bServiceDirty = FALSE;
478 : }
479 :
480 2 : CPLDestroyXMLNode( psService );
481 :
482 2 : CPLFree( pszProjection );
483 2 : pszProjection = NULL;
484 :
485 2 : CSLDestroy( papszHttpOptions );
486 2 : CSLDestroy( papszSDSModifiers );
487 :
488 2 : CPLFree( apszCoverageOfferingMD[0] );
489 :
490 2 : FlushMemoryResult();
491 2 : }
492 :
493 : /************************************************************************/
494 : /* TestUseBlockIO() */
495 : /* */
496 : /* Check whether we should use blocked IO (true) or direct io */
497 : /* (FALSE) for a given request configuration and environment. */
498 : /************************************************************************/
499 :
500 598 : int WCSDataset::TestUseBlockIO( int nXOff, int nYOff, int nXSize, int nYSize,
501 : int nBufXSize, int nBufYSize )
502 :
503 : {
504 598 : int bUseBlockedIO = bForceCachedIO;
505 :
506 598 : if( nYSize == 1 || nXSize * ((double) nYSize) < 100.0 )
507 598 : bUseBlockedIO = TRUE;
508 :
509 598 : if( nBufYSize == 1 || nBufXSize * ((double) nBufYSize) < 100.0 )
510 598 : bUseBlockedIO = TRUE;
511 :
512 598 : if( bUseBlockedIO
513 : && CSLTestBoolean( CPLGetConfigOption( "GDAL_ONE_BIG_READ", "NO") ) )
514 0 : bUseBlockedIO = FALSE;
515 :
516 598 : return bUseBlockedIO;
517 : }
518 :
519 : /************************************************************************/
520 : /* IRasterIO() */
521 : /************************************************************************/
522 :
523 0 : CPLErr WCSDataset::IRasterIO( GDALRWFlag eRWFlag,
524 : int nXOff, int nYOff, int nXSize, int nYSize,
525 : void * pData, int nBufXSize, int nBufYSize,
526 : GDALDataType eBufType,
527 : int nBandCount, int *panBandMap,
528 : int nPixelSpace, int nLineSpace, int nBandSpace)
529 :
530 : {
531 0 : if( (nMaxCols > 0 && nMaxCols < nBufXSize)
532 : || (nMaxRows > 0 && nMaxRows < nBufYSize) )
533 0 : return CE_Failure;
534 :
535 : /* -------------------------------------------------------------------- */
536 : /* We need various criteria to skip out to block based methods. */
537 : /* -------------------------------------------------------------------- */
538 0 : if( TestUseBlockIO( nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize ) )
539 : return GDALPamDataset::IRasterIO(
540 : eRWFlag, nXOff, nYOff, nXSize, nYSize,
541 : pData, nBufXSize, nBufYSize, eBufType,
542 0 : nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace );
543 : else
544 : return DirectRasterIO(
545 : eRWFlag, nXOff, nYOff, nXSize, nYSize,
546 : pData, nBufXSize, nBufYSize, eBufType,
547 0 : nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace );
548 : }
549 :
550 : /************************************************************************/
551 : /* DirectRasterIO() */
552 : /* */
553 : /* Make exactly one request to the server for this data. */
554 : /************************************************************************/
555 :
556 : CPLErr
557 0 : WCSDataset::DirectRasterIO( GDALRWFlag eRWFlag,
558 : int nXOff, int nYOff, int nXSize, int nYSize,
559 : void * pData, int nBufXSize, int nBufYSize,
560 : GDALDataType eBufType,
561 : int nBandCount, int *panBandMap,
562 : int nPixelSpace, int nLineSpace, int nBandSpace)
563 :
564 : {
565 : CPLDebug( "WCS", "DirectRasterIO(%d,%d,%d,%d) -> (%d,%d) (%d bands)\n",
566 : nXOff, nYOff, nXSize, nYSize,
567 0 : nBufXSize, nBufYSize, nBandCount );
568 :
569 : /* -------------------------------------------------------------------- */
570 : /* Get the coverage. */
571 : /* -------------------------------------------------------------------- */
572 0 : CPLHTTPResult *psResult = NULL;
573 : CPLErr eErr =
574 : GetCoverage( nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
575 0 : nBandCount, panBandMap, &psResult );
576 :
577 0 : if( eErr != CE_None )
578 0 : return eErr;
579 :
580 : /* -------------------------------------------------------------------- */
581 : /* Try and open result as a dataseat. */
582 : /* -------------------------------------------------------------------- */
583 0 : GDALDataset *poTileDS = GDALOpenResult( psResult );
584 :
585 0 : if( poTileDS == NULL )
586 0 : return CE_Failure;
587 :
588 : /* -------------------------------------------------------------------- */
589 : /* Verify configuration. */
590 : /* -------------------------------------------------------------------- */
591 0 : if( poTileDS->GetRasterXSize() != nBufXSize
592 : || poTileDS->GetRasterYSize() != nBufYSize )
593 : {
594 : CPLDebug( "WCS", "Got size=%dx%d instead of %dx%d.",
595 : poTileDS->GetRasterXSize(), poTileDS->GetRasterYSize(),
596 0 : nBufXSize, nBufYSize );
597 :
598 : CPLError( CE_Failure, CPLE_AppDefined,
599 : "Returned tile does not match expected configuration.\n"
600 : "Got %dx%d instead of %dx%d.",
601 : poTileDS->GetRasterXSize(), poTileDS->GetRasterYSize(),
602 0 : nBufXSize, nBufYSize );
603 0 : delete poTileDS;
604 0 : return CE_Failure;
605 : }
606 :
607 0 : if( (strlen(osBandIdentifier) && poTileDS->GetRasterCount() != nBandCount)
608 : || (!strlen(osBandIdentifier) && poTileDS->GetRasterCount() !=
609 : GetRasterCount() ) )
610 : {
611 : CPLError( CE_Failure, CPLE_AppDefined,
612 0 : "Returned tile does not match expected band count." );
613 0 : delete poTileDS;
614 0 : return CE_Failure;
615 : }
616 :
617 : /* -------------------------------------------------------------------- */
618 : /* Pull requested bands from the downloaded dataset. */
619 : /* -------------------------------------------------------------------- */
620 : int iBand;
621 :
622 0 : eErr = CE_None;
623 :
624 0 : for( iBand = 0;
625 : iBand < nBandCount && eErr == CE_None;
626 : iBand++ )
627 : {
628 : GDALRasterBand *poTileBand;
629 :
630 0 : if( strlen(osBandIdentifier) )
631 0 : poTileBand = poTileDS->GetRasterBand( iBand + 1 );
632 : else
633 0 : poTileBand = poTileDS->GetRasterBand( panBandMap[iBand] );
634 :
635 : eErr = poTileBand->RasterIO( GF_Read,
636 : 0, 0, nBufXSize, nBufYSize,
637 : ((GByte *) pData) +
638 : iBand * nBandSpace, nBufXSize, nBufYSize,
639 0 : eBufType, nPixelSpace, nLineSpace );
640 : }
641 :
642 : /* -------------------------------------------------------------------- */
643 : /* Cleanup */
644 : /* -------------------------------------------------------------------- */
645 0 : delete poTileDS;
646 :
647 0 : FlushMemoryResult();
648 :
649 0 : return eErr;
650 : }
651 :
652 : /************************************************************************/
653 : /* GetCoverage() */
654 : /* */
655 : /* Issue the appropriate version of request for a given window, */
656 : /* buffer size and band list. */
657 : /************************************************************************/
658 :
659 3 : CPLErr WCSDataset::GetCoverage( int nXOff, int nYOff, int nXSize, int nYSize,
660 : int nBufXSize, int nBufYSize,
661 : int nBandCount, int *panBandList,
662 : CPLHTTPResult **ppsResult )
663 :
664 : {
665 3 : CPLLocaleC oLocaleEnforcer;
666 :
667 : /* -------------------------------------------------------------------- */
668 : /* Figure out the georeferenced extents. */
669 : /* -------------------------------------------------------------------- */
670 : double dfMinX, dfMaxX, dfMinY, dfMaxY;
671 :
672 : // WCS 1.0 extents are the outer edges of outer pixels.
673 3 : dfMinX = adfGeoTransform[0] +
674 3 : (nXOff) * adfGeoTransform[1];
675 3 : dfMaxX = adfGeoTransform[0] +
676 3 : (nXOff + nXSize) * adfGeoTransform[1];
677 3 : dfMaxY = adfGeoTransform[3] +
678 3 : (nYOff) * adfGeoTransform[5];
679 3 : dfMinY = adfGeoTransform[3] +
680 3 : (nYOff + nYSize) * adfGeoTransform[5];
681 :
682 : /* -------------------------------------------------------------------- */
683 : /* Build band list if we have the band identifier. */
684 : /* -------------------------------------------------------------------- */
685 3 : CPLString osBandList;
686 3 : int bSelectingBands = FALSE;
687 :
688 3 : if( strlen(osBandIdentifier) && nBandCount > 0 )
689 : {
690 : int iBand;
691 :
692 0 : for( iBand = 0; iBand < nBandCount; iBand++ )
693 : {
694 0 : if( iBand > 0 )
695 0 : osBandList += ",";
696 0 : osBandList += CPLString().Printf( "%d", panBandList[iBand] );
697 : }
698 :
699 0 : bSelectingBands = TRUE;
700 : }
701 :
702 : /* -------------------------------------------------------------------- */
703 : /* URL encode strings that could have questionable characters. */
704 : /* -------------------------------------------------------------------- */
705 3 : CPLString osCoverage, osFormat;
706 : char *pszEncoded;
707 :
708 3 : osCoverage = CPLGetXMLValue( psService, "CoverageName", "" );
709 :
710 3 : pszEncoded = CPLEscapeString( osCoverage, -1, CPLES_URL );
711 3 : osCoverage = pszEncoded;
712 3 : CPLFree( pszEncoded );
713 :
714 3 : osFormat = CPLGetXMLValue( psService, "PreferredFormat", "" );
715 :
716 3 : pszEncoded = CPLEscapeString( osFormat, -1, CPLES_URL );
717 3 : osFormat = pszEncoded;
718 3 : CPLFree( pszEncoded );
719 :
720 : /* -------------------------------------------------------------------- */
721 : /* Do we have a time we want to use? */
722 : /* -------------------------------------------------------------------- */
723 3 : CPLString osTime;
724 :
725 3 : osTime = CSLFetchNameValueDef( papszSDSModifiers, "time", osDefaultTime );
726 :
727 : /* -------------------------------------------------------------------- */
728 : /* Construct a "simple" GetCoverage request (WCS 1.0). */
729 : /* -------------------------------------------------------------------- */
730 3 : CPLString osRequest;
731 :
732 3 : if( nVersion == 100 )
733 : {
734 : osRequest.Printf(
735 : "%sSERVICE=WCS&VERSION=1.0.0&REQUEST=GetCoverage&COVERAGE=%s"
736 : "&FORMAT=%s&BBOX=%.15g,%.15g,%.15g,%.15g&WIDTH=%d&HEIGHT=%d&CRS=%s%s",
737 : CPLGetXMLValue( psService, "ServiceURL", "" ),
738 : osCoverage.c_str(),
739 : osFormat.c_str(),
740 : dfMinX, dfMinY, dfMaxX, dfMaxY,
741 : nBufXSize, nBufYSize,
742 : osCRS.c_str(),
743 3 : CPLGetXMLValue( psService, "GetCoverageExtra", "" ) );
744 :
745 3 : if( CPLGetXMLValue( psService, "Resample", NULL ) )
746 : {
747 0 : osRequest += "&INTERPOLATION=";
748 0 : osRequest += CPLGetXMLValue( psService, "Resample", "" );
749 : }
750 :
751 3 : if( osTime != "" )
752 : {
753 0 : osRequest += "&time=";
754 0 : osRequest += osTime;
755 : }
756 :
757 3 : if( bSelectingBands )
758 : {
759 : osRequest += CPLString().Printf( "&%s=%s",
760 : osBandIdentifier.c_str(),
761 0 : osBandList.c_str() );
762 : }
763 : }
764 :
765 : /* -------------------------------------------------------------------- */
766 : /* Construct a "simple" GetCoverage request (WCS 1.1+). */
767 : /* -------------------------------------------------------------------- */
768 : else
769 : {
770 0 : CPLString osRangeSubset;
771 :
772 : osRangeSubset.Printf("&RangeSubset=%s",
773 0 : CPLGetXMLValue(psService,"FieldName",""));
774 :
775 0 : if( CPLGetXMLValue( psService, "Resample", NULL ) )
776 : {
777 0 : osRangeSubset += ":";
778 0 : osRangeSubset += CPLGetXMLValue( psService, "Resample", "");
779 : }
780 :
781 0 : if( bSelectingBands )
782 : {
783 : osRangeSubset +=
784 : CPLString().Printf( "[%s[%s]]",
785 : osBandIdentifier.c_str(),
786 0 : osBandList.c_str() );
787 : }
788 :
789 : // WCS 1.1 extents are centers of outer pixels.
790 0 : dfMaxX -= adfGeoTransform[1] * 0.5;
791 0 : dfMinX += adfGeoTransform[1] * 0.5;
792 0 : dfMinY -= adfGeoTransform[5] * 0.5;
793 0 : dfMaxY += adfGeoTransform[5] * 0.5;
794 :
795 : // Carefully adjust bounds for pixel centered values at new
796 : // sampling density.
797 :
798 0 : double dfXStep = adfGeoTransform[1];
799 0 : double dfYStep = adfGeoTransform[5];
800 :
801 0 : if( nBufXSize != nXSize || nBufYSize != nYSize )
802 : {
803 0 : dfXStep = (nXSize/(double)nBufXSize) * adfGeoTransform[1];
804 0 : dfYStep = (nYSize/(double)nBufYSize) * adfGeoTransform[5];
805 :
806 0 : dfMinX = nXOff * adfGeoTransform[1] + adfGeoTransform[0]
807 0 : + dfXStep * 0.5;
808 0 : dfMaxX = dfMinX + (nBufXSize - 1) * dfXStep;
809 :
810 0 : dfMaxY = nYOff * adfGeoTransform[5] + adfGeoTransform[3]
811 0 : + dfYStep * 0.5;
812 0 : dfMinY = dfMaxY + (nBufYSize - 1) * dfYStep;
813 : }
814 :
815 : osRequest.Printf(
816 : "%sSERVICE=WCS&VERSION=%s&REQUEST=GetCoverage&IDENTIFIER=%s"
817 : "&FORMAT=%s&BOUNDINGBOX=%.15g,%.15g,%.15g,%.15g,%s%s%s",
818 : CPLGetXMLValue( psService, "ServiceURL", "" ),
819 : CPLGetXMLValue( psService, "Version", "" ),
820 : osCoverage.c_str(),
821 : osFormat.c_str(),
822 : dfMinX, dfMinY, dfMaxX, dfMaxY,
823 : osCRS.c_str(),
824 : osRangeSubset.c_str(),
825 0 : CPLGetXMLValue( psService, "GetCoverageExtra", "" ) );
826 :
827 0 : if( nBufXSize != nXSize || nBufYSize != nYSize )
828 : {
829 : osRequest += CPLString().Printf(
830 : "&GridBaseCRS=%s"
831 : "&GridCS=%s"
832 : "&GridType=urn:ogc:def:method:WCS:1.1:2dGridIn2dCrs"
833 : "&GridOrigin=%.15g,%.15g"
834 : "&GridOffsets=%.15g,%.15g",
835 : osCRS.c_str(),
836 : osCRS.c_str(),
837 : dfMinX, dfMaxY,
838 0 : dfXStep, dfYStep );
839 0 : }
840 : }
841 :
842 : /* -------------------------------------------------------------------- */
843 : /* Fetch the result. */
844 : /* -------------------------------------------------------------------- */
845 3 : CPLErrorReset();
846 :
847 3 : *ppsResult = CPLHTTPFetch( osRequest, papszHttpOptions );
848 :
849 3 : if( ProcessError( *ppsResult ) )
850 0 : return CE_Failure;
851 : else
852 3 : return CE_None;
853 : }
854 :
855 : /************************************************************************/
856 : /* DescribeCoverage() */
857 : /* */
858 : /* Fetch the DescribeCoverage result and attach it to the */
859 : /* service description. */
860 : /************************************************************************/
861 :
862 2 : int WCSDataset::DescribeCoverage()
863 :
864 : {
865 2 : CPLString osRequest;
866 :
867 : /* -------------------------------------------------------------------- */
868 : /* Fetch coverage description for this coverage. */
869 : /* -------------------------------------------------------------------- */
870 2 : if( nVersion == 100 )
871 : osRequest.Printf(
872 : "%sSERVICE=WCS&REQUEST=DescribeCoverage&VERSION=%s&COVERAGE=%s%s",
873 : CPLGetXMLValue( psService, "ServiceURL", "" ),
874 : CPLGetXMLValue( psService, "Version", "1.0.0" ),
875 : CPLGetXMLValue( psService, "CoverageName", "" ),
876 2 : CPLGetXMLValue( psService, "DescribeCoverageExtra", "" ) );
877 : else
878 : osRequest.Printf(
879 : "%sSERVICE=WCS&REQUEST=DescribeCoverage&VERSION=%s&IDENTIFIERS=%s%s&FORMAT=text/xml",
880 : CPLGetXMLValue( psService, "ServiceURL", "" ),
881 : CPLGetXMLValue( psService, "Version", "1.0.0" ),
882 : CPLGetXMLValue( psService, "CoverageName", "" ),
883 0 : CPLGetXMLValue( psService, "DescribeCoverageExtra", "" ) );
884 :
885 2 : CPLErrorReset();
886 :
887 2 : CPLHTTPResult *psResult = CPLHTTPFetch( osRequest, papszHttpOptions );
888 :
889 2 : if( ProcessError( psResult ) )
890 0 : return FALSE;
891 :
892 : /* -------------------------------------------------------------------- */
893 : /* Parse result. */
894 : /* -------------------------------------------------------------------- */
895 2 : CPLXMLNode *psDC = CPLParseXMLString( (const char *) psResult->pabyData );
896 2 : CPLHTTPDestroyResult( psResult );
897 :
898 2 : if( psDC == NULL )
899 0 : return FALSE;
900 :
901 2 : CPLStripXMLNamespace( psDC, NULL, TRUE );
902 :
903 : /* -------------------------------------------------------------------- */
904 : /* Did we get a CoverageOffering? */
905 : /* -------------------------------------------------------------------- */
906 : CPLXMLNode *psCO;
907 :
908 2 : if( nVersion == 100 )
909 2 : psCO = CPLGetXMLNode( psDC, "=CoverageDescription.CoverageOffering" );
910 : else
911 0 : psCO =CPLGetXMLNode( psDC,"=CoverageDescriptions.CoverageDescription");
912 :
913 2 : if( !psCO )
914 : {
915 0 : CPLDestroyXMLNode( psDC );
916 :
917 : CPLError( CE_Failure, CPLE_AppDefined,
918 : "Failed to fetch a <CoverageOffering> back %s.",
919 0 : osRequest.c_str() );
920 0 : return FALSE;
921 : }
922 :
923 : /* -------------------------------------------------------------------- */
924 : /* Duplicate the coverage offering, and insert into */
925 : /* -------------------------------------------------------------------- */
926 2 : CPLXMLNode *psNext = psCO->psNext;
927 2 : psCO->psNext = NULL;
928 :
929 2 : CPLAddXMLChild( psService, CPLCloneXMLTree( psCO ) );
930 2 : bServiceDirty = TRUE;
931 :
932 2 : psCO->psNext = psNext;
933 :
934 2 : CPLDestroyXMLNode( psDC );
935 2 : return TRUE;
936 : }
937 :
938 : /************************************************************************/
939 : /* ExtractGridInfo100() */
940 : /* */
941 : /* Collect info about grid from describe coverage for WCS 1.0.0 */
942 : /* and above. */
943 : /************************************************************************/
944 :
945 2 : int WCSDataset::ExtractGridInfo100()
946 :
947 : {
948 2 : CPLXMLNode * psCO = CPLGetXMLNode( psService, "CoverageOffering" );
949 :
950 2 : if( psCO == NULL )
951 0 : return FALSE;
952 :
953 : /* -------------------------------------------------------------------- */
954 : /* We need to strip off name spaces so it is easier to */
955 : /* searchfor plain gml names. */
956 : /* -------------------------------------------------------------------- */
957 2 : CPLStripXMLNamespace( psCO, NULL, TRUE );
958 :
959 : /* -------------------------------------------------------------------- */
960 : /* Verify we have a Rectified Grid. */
961 : /* -------------------------------------------------------------------- */
962 : CPLXMLNode *psRG =
963 2 : CPLGetXMLNode( psCO, "domainSet.spatialDomain.RectifiedGrid" );
964 :
965 2 : if( psRG == NULL )
966 : {
967 : CPLError( CE_Failure, CPLE_AppDefined,
968 : "Unable to find RectifiedGrid in CoverageOffering,\n"
969 0 : "unable to process WCS Coverage." );
970 0 : return FALSE;
971 : }
972 :
973 : /* -------------------------------------------------------------------- */
974 : /* Extract size, geotransform and coordinate system. */
975 : /* -------------------------------------------------------------------- */
976 2 : if( GDALParseGMLCoverage( psRG, &nRasterXSize, &nRasterYSize,
977 : adfGeoTransform, &pszProjection ) != CE_None )
978 0 : return FALSE;
979 :
980 : /* -------------------------------------------------------------------- */
981 : /* Fallback to nativeCRSs declaration. */
982 : /* -------------------------------------------------------------------- */
983 : const char *pszNativeCRSs =
984 2 : CPLGetXMLValue( psCO, "supportedCRSs.nativeCRSs", NULL );
985 :
986 2 : if( pszNativeCRSs == NULL )
987 : pszNativeCRSs =
988 2 : CPLGetXMLValue( psCO, "supportedCRSs.requestResponseCRSs", NULL );
989 :
990 2 : if( pszNativeCRSs == NULL )
991 : pszNativeCRSs =
992 0 : CPLGetXMLValue( psCO, "supportedCRSs.requestCRSs", NULL );
993 :
994 2 : if( pszNativeCRSs == NULL )
995 : pszNativeCRSs =
996 0 : CPLGetXMLValue( psCO, "supportedCRSs.responseCRSs", NULL );
997 :
998 2 : if( pszNativeCRSs != NULL
999 : && (pszProjection == NULL || strlen(pszProjection) == 0) )
1000 : {
1001 2 : OGRSpatialReference oSRS;
1002 :
1003 2 : if( oSRS.SetFromUserInput( pszNativeCRSs ) == OGRERR_NONE )
1004 : {
1005 2 : CPLFree( pszProjection );
1006 2 : oSRS.exportToWkt( &pszProjection );
1007 : }
1008 : else
1009 : CPLDebug( "WCS",
1010 : "<nativeCRSs> element contents not parsable:\n%s",
1011 0 : pszNativeCRSs );
1012 : }
1013 :
1014 : // We should try to use the services name for the CRS if possible.
1015 2 : if( pszNativeCRSs != NULL
1016 : && ( EQUALN(pszNativeCRSs,"EPSG:",5)
1017 : || EQUALN(pszNativeCRSs,"AUTO:",5)
1018 : || EQUALN(pszNativeCRSs,"Image ",6)
1019 : || EQUALN(pszNativeCRSs,"Engineering ",12)
1020 : || EQUALN(pszNativeCRSs,"OGC:",4) ) )
1021 : {
1022 2 : osCRS = pszNativeCRSs;
1023 :
1024 2 : size_t nDivider = osCRS.find( " " );
1025 :
1026 2 : if( nDivider != std::string::npos )
1027 0 : osCRS.resize( nDivider-1 );
1028 : }
1029 :
1030 : /* -------------------------------------------------------------------- */
1031 : /* Do we have a coordinate system override? */
1032 : /* -------------------------------------------------------------------- */
1033 2 : const char *pszProjOverride = CPLGetXMLValue( psService, "SRS", NULL );
1034 :
1035 2 : if( pszProjOverride )
1036 : {
1037 0 : OGRSpatialReference oSRS;
1038 :
1039 0 : if( oSRS.SetFromUserInput( pszProjOverride ) != OGRERR_NONE )
1040 : {
1041 : CPLError( CE_Failure, CPLE_AppDefined,
1042 : "<SRS> element contents not parsable:\n%s",
1043 0 : pszProjOverride );
1044 0 : return FALSE;
1045 : }
1046 :
1047 0 : CPLFree( pszProjection );
1048 0 : oSRS.exportToWkt( &pszProjection );
1049 :
1050 0 : if( EQUALN(pszProjOverride,"EPSG:",5)
1051 : || EQUALN(pszProjOverride,"AUTO:",5)
1052 : || EQUALN(pszProjOverride,"OGC:",4)
1053 : || EQUALN(pszProjOverride,"Image ",6)
1054 : || EQUALN(pszProjOverride,"Engineering ",12) )
1055 0 : osCRS = pszProjOverride;
1056 : }
1057 :
1058 : /* -------------------------------------------------------------------- */
1059 : /* Build CRS name to use. */
1060 : /* -------------------------------------------------------------------- */
1061 2 : OGRSpatialReference oSRS;
1062 : const char *pszAuth;
1063 :
1064 2 : if( pszProjection && strlen(pszProjection) > 0 && osCRS == "" )
1065 : {
1066 0 : oSRS.SetFromUserInput( pszProjection );
1067 0 : pszAuth = oSRS.GetAuthorityName(NULL);
1068 :
1069 0 : if( pszAuth != NULL && EQUAL(pszAuth,"EPSG") )
1070 : {
1071 0 : pszAuth = oSRS.GetAuthorityCode(NULL);
1072 0 : if( pszAuth )
1073 : {
1074 0 : osCRS = "EPSG:";
1075 0 : osCRS += pszAuth;
1076 : }
1077 : else
1078 : {
1079 : CPLError( CE_Failure, CPLE_AppDefined,
1080 0 : "Unable to define CRS to use." );
1081 0 : return FALSE;
1082 : }
1083 : }
1084 : }
1085 :
1086 : /* -------------------------------------------------------------------- */
1087 : /* Pick a format type if we don't already have one selected. */
1088 : /* */
1089 : /* We will prefer anything that sounds like TIFF, otherwise */
1090 : /* falling back to the first supported format. Should we */
1091 : /* consider preferring the nativeFormat if available? */
1092 : /* -------------------------------------------------------------------- */
1093 2 : if( CPLGetXMLValue( psService, "PreferredFormat", NULL ) == NULL )
1094 : {
1095 2 : CPLXMLNode *psSF = CPLGetXMLNode( psCO, "supportedFormats" );
1096 : CPLXMLNode *psNode;
1097 2 : char **papszFormatList = NULL;
1098 2 : CPLString osPreferredFormat;
1099 : int iFormat;
1100 :
1101 2 : if( psSF == NULL )
1102 : {
1103 : CPLError( CE_Failure, CPLE_AppDefined,
1104 : "No <PreferredFormat> tag in service definition file, and no\n"
1105 0 : "<supportedFormats> in coverageOffering." );
1106 0 : return FALSE;
1107 : }
1108 :
1109 12 : for( psNode = psSF->psChild; psNode != NULL; psNode = psNode->psNext )
1110 : {
1111 10 : if( psNode->eType == CXT_Element
1112 : && EQUAL(psNode->pszValue,"formats")
1113 : && psNode->psChild != NULL
1114 : && psNode->psChild->eType == CXT_Text )
1115 : {
1116 : // This check is looking for deprecated WCS 1.0 capabilities
1117 : // with multiple formats space delimited in a single <formats>
1118 : // element per GDAL ticket 1748 (done by MapServer 4.10 and
1119 : // earlier for instance).
1120 8 : if( papszFormatList == NULL
1121 : && psNode->psNext == NULL
1122 : && strstr(psNode->psChild->pszValue," ") != NULL
1123 : && strstr(psNode->psChild->pszValue,";") == NULL )
1124 : {
1125 : char **papszSubList =
1126 0 : CSLTokenizeString( psNode->psChild->pszValue );
1127 : papszFormatList = CSLInsertStrings( papszFormatList,
1128 0 : -1, papszSubList );
1129 0 : CSLDestroy( papszSubList );
1130 : }
1131 : else
1132 : {
1133 : papszFormatList = CSLAddString( papszFormatList,
1134 8 : psNode->psChild->pszValue);
1135 : }
1136 : }
1137 : }
1138 :
1139 4 : for( iFormat = 0;
1140 2 : papszFormatList != NULL && papszFormatList[iFormat] != NULL;
1141 : iFormat++ )
1142 : {
1143 2 : if( strlen(osPreferredFormat) == 0 )
1144 2 : osPreferredFormat = papszFormatList[iFormat];
1145 :
1146 2 : if( strstr(papszFormatList[iFormat],"tiff") != NULL
1147 : || strstr(papszFormatList[iFormat],"TIFF") != NULL
1148 : || strstr(papszFormatList[iFormat],"Tiff") != NULL )
1149 : {
1150 2 : osPreferredFormat = papszFormatList[iFormat];
1151 2 : break;
1152 : }
1153 : }
1154 :
1155 2 : CSLDestroy( papszFormatList );
1156 :
1157 2 : if( strlen(osPreferredFormat) > 0 )
1158 : {
1159 2 : bServiceDirty = TRUE;
1160 : CPLCreateXMLElementAndValue( psService, "PreferredFormat",
1161 2 : osPreferredFormat );
1162 0 : }
1163 : }
1164 :
1165 : /* -------------------------------------------------------------------- */
1166 : /* Try to identify a nodata value. For now we only support the */
1167 : /* singleValue mechanism. */
1168 : /* -------------------------------------------------------------------- */
1169 2 : if( CPLGetXMLValue( psService, "NoDataValue", NULL ) == NULL )
1170 : {
1171 2 : const char *pszSV = CPLGetXMLValue( psCO, "rangeSet.RangeSet.nullValues.singleValue", NULL );
1172 :
1173 2 : if( pszSV != NULL && (atof(pszSV) != 0.0 || *pszSV == '0') )
1174 : {
1175 0 : bServiceDirty = TRUE;
1176 : CPLCreateXMLElementAndValue( psService, "NoDataValue",
1177 0 : pszSV );
1178 : }
1179 : }
1180 :
1181 : /* -------------------------------------------------------------------- */
1182 : /* Do we have a Band range type. For now we look for a fairly */
1183 : /* specific configuration. The rangeset my have one axis named */
1184 : /* "Band", with a set of ascending numerical values. */
1185 : /* -------------------------------------------------------------------- */
1186 2 : osBandIdentifier = CPLGetXMLValue( psService, "BandIdentifier", "" );
1187 : CPLXMLNode * psAD = CPLGetXMLNode( psService,
1188 2 : "CoverageOffering.rangeSet.RangeSet.axisDescription.AxisDescription" );
1189 : CPLXMLNode *psValues;
1190 :
1191 2 : if( strlen(osBandIdentifier) == 0
1192 : && psAD != NULL
1193 : && (EQUAL(CPLGetXMLValue(psAD,"name",""),"Band")
1194 : || EQUAL(CPLGetXMLValue(psAD,"name",""),"Bands"))
1195 : && ( (psValues = CPLGetXMLNode( psAD, "values" )) != NULL ) )
1196 : {
1197 : CPLXMLNode *psSV;
1198 : int iBand;
1199 :
1200 2 : osBandIdentifier = CPLGetXMLValue(psAD,"name","");
1201 :
1202 2 : for( psSV = psValues->psChild, iBand = 1;
1203 : psSV != NULL;
1204 : psSV = psSV->psNext, iBand++ )
1205 : {
1206 2 : if( psSV->eType != CXT_Element
1207 : || !EQUAL(psSV->pszValue,"singleValue")
1208 : || psSV->psChild == NULL
1209 : || psSV->psChild->eType != CXT_Text
1210 : || atoi(psSV->psChild->pszValue) != iBand )
1211 : {
1212 2 : osBandIdentifier = "";
1213 2 : break;
1214 : }
1215 : }
1216 :
1217 2 : if( strlen(osBandIdentifier) )
1218 : {
1219 0 : bServiceDirty = TRUE;
1220 : CPLCreateXMLElementAndValue( psService, "BandIdentifier",
1221 0 : osBandIdentifier );
1222 : }
1223 : }
1224 :
1225 : /* -------------------------------------------------------------------- */
1226 : /* Do we have a temporal domain? If so, try to identify a */
1227 : /* default time value. */
1228 : /* -------------------------------------------------------------------- */
1229 2 : osDefaultTime = CPLGetXMLValue( psService, "DefaultTime", "" );
1230 : CPLXMLNode * psTD =
1231 2 : CPLGetXMLNode( psService, "CoverageOffering.domainSet.temporalDomain" );
1232 2 : CPLString osServiceURL = CPLGetXMLValue( psService, "ServiceURL", "" );
1233 2 : CPLString osCoverageExtra = CPLGetXMLValue( psService, "GetCoverageExtra", "" );
1234 :
1235 2 : if( psTD != NULL )
1236 : {
1237 : CPLXMLNode *psTime;
1238 :
1239 : // collect all the allowed time positions.
1240 :
1241 0 : for( psTime = psTD->psChild; psTime != NULL; psTime = psTime->psNext )
1242 : {
1243 0 : if( psTime->eType == CXT_Element
1244 : && EQUAL(psTime->pszValue,"timePosition")
1245 : && psTime->psChild != NULL
1246 : && psTime->psChild->eType == CXT_Text )
1247 0 : aosTimePositions.push_back( psTime->psChild->pszValue );
1248 : }
1249 :
1250 : // we will default to the last - likely the most recent - entry.
1251 :
1252 0 : if( aosTimePositions.size() > 0
1253 : && osDefaultTime == ""
1254 : && osServiceURL.ifind("time=") == std::string::npos
1255 : && osCoverageExtra.ifind("time=") == std::string::npos )
1256 : {
1257 0 : osDefaultTime = aosTimePositions[aosTimePositions.size()-1];
1258 :
1259 0 : bServiceDirty = TRUE;
1260 : CPLCreateXMLElementAndValue( psService, "DefaultTime",
1261 0 : osDefaultTime );
1262 : }
1263 : }
1264 :
1265 2 : return TRUE;
1266 : }
1267 :
1268 : /************************************************************************/
1269 : /* ParseBoundingBox() */
1270 : /************************************************************************/
1271 :
1272 0 : static int ParseBoundingBox( CPLXMLNode *psBoundingBox, CPLString &osCRS,
1273 : double &dfLowerX, double &dfLowerY,
1274 : double &dfUpperX, double &dfUpperY )
1275 :
1276 : {
1277 0 : int nRet = TRUE;
1278 :
1279 0 : osCRS = CPLGetXMLValue( psBoundingBox, "crs", "" );
1280 :
1281 : char **papszLC = CSLTokenizeStringComplex(
1282 : CPLGetXMLValue( psBoundingBox, "LowerCorner", ""),
1283 0 : " ", FALSE, FALSE );
1284 : char **papszUC = CSLTokenizeStringComplex(
1285 : CPLGetXMLValue( psBoundingBox, "UpperCorner", ""),
1286 0 : " ", FALSE, FALSE );
1287 :
1288 0 : if( CSLCount(papszLC) >= 2 && CSLCount(papszUC) >= 2 )
1289 : {
1290 0 : dfLowerX = atof(papszLC[0]);
1291 0 : dfLowerY = atof(papszLC[1]);
1292 0 : dfUpperX = atof(papszUC[0]);
1293 0 : dfUpperY = atof(papszUC[1]);
1294 : }
1295 : else
1296 0 : nRet = FALSE;
1297 :
1298 0 : CSLDestroy( papszUC );
1299 0 : CSLDestroy( papszLC );
1300 :
1301 0 : return nRet;
1302 : }
1303 :
1304 : /************************************************************************/
1305 : /* ExtractGridInfo() */
1306 : /* */
1307 : /* Collect info about grid from describe coverage for WCS 1.1 */
1308 : /* and above. */
1309 : /************************************************************************/
1310 :
1311 2 : int WCSDataset::ExtractGridInfo()
1312 :
1313 : {
1314 2 : if( nVersion == 100 )
1315 2 : return ExtractGridInfo100();
1316 :
1317 0 : CPLXMLNode * psCO = CPLGetXMLNode( psService, "CoverageDescription" );
1318 :
1319 0 : if( psCO == NULL )
1320 0 : return FALSE;
1321 :
1322 : /* -------------------------------------------------------------------- */
1323 : /* We need to strip off name spaces so it is easier to */
1324 : /* searchfor plain gml names. */
1325 : /* -------------------------------------------------------------------- */
1326 0 : CPLStripXMLNamespace( psCO, NULL, TRUE );
1327 :
1328 : /* -------------------------------------------------------------------- */
1329 : /* Verify we have a SpatialDomain and GridCRS. */
1330 : /* -------------------------------------------------------------------- */
1331 : CPLXMLNode *psSD =
1332 0 : CPLGetXMLNode( psCO, "Domain.SpatialDomain" );
1333 : CPLXMLNode *psGCRS =
1334 0 : CPLGetXMLNode( psSD, "GridCRS" );
1335 :
1336 0 : if( psSD == NULL || psGCRS == NULL )
1337 : {
1338 : CPLError( CE_Failure, CPLE_AppDefined,
1339 : "Unable to find GridCRS in CoverageDescription,\n"
1340 0 : "unable to process WCS Coverage." );
1341 0 : return FALSE;
1342 : }
1343 :
1344 : /* -------------------------------------------------------------------- */
1345 : /* Extract Geotransform from GridCRS. */
1346 : /* -------------------------------------------------------------------- */
1347 : const char *pszGridType = CPLGetXMLValue( psGCRS, "GridType",
1348 0 : "urn:ogc:def:method:WCS::2dSimpleGrid" );
1349 :
1350 : char **papszOriginTokens =
1351 : CSLTokenizeStringComplex( CPLGetXMLValue( psGCRS, "GridOrigin", ""),
1352 0 : " ", FALSE, FALSE );
1353 : char **papszOffsetTokens =
1354 : CSLTokenizeStringComplex( CPLGetXMLValue( psGCRS, "GridOffsets", ""),
1355 0 : " ", FALSE, FALSE );
1356 :
1357 0 : if( strstr(pszGridType,":2dGridIn2dCrs")
1358 : || strstr(pszGridType,":2dGridin2dCrs") )
1359 : {
1360 0 : if( CSLCount(papszOffsetTokens) == 4
1361 : && CSLCount(papszOriginTokens) == 2 )
1362 : {
1363 0 : adfGeoTransform[0] = atof(papszOriginTokens[0]);
1364 0 : adfGeoTransform[1] = atof(papszOffsetTokens[0]);
1365 0 : adfGeoTransform[2] = atof(papszOffsetTokens[1]);
1366 0 : adfGeoTransform[3] = atof(papszOriginTokens[1]);
1367 0 : adfGeoTransform[4] = atof(papszOffsetTokens[2]);
1368 0 : adfGeoTransform[5] = atof(papszOffsetTokens[3]);
1369 : }
1370 : else
1371 : {
1372 : CPLError( CE_Failure, CPLE_AppDefined,
1373 : "2dGridIn2dCrs does not have expected GridOrigin or\n"
1374 0 : "GridOffsets values - unable to process WCS coverage.");
1375 0 : return FALSE;
1376 : }
1377 : }
1378 :
1379 0 : else if( strstr(pszGridType,":2dGridIn3dCrs") )
1380 : {
1381 0 : if( CSLCount(papszOffsetTokens) == 6
1382 : && CSLCount(papszOriginTokens) == 3 )
1383 : {
1384 0 : adfGeoTransform[0] = atof(papszOriginTokens[0]);
1385 0 : adfGeoTransform[1] = atof(papszOffsetTokens[0]);
1386 0 : adfGeoTransform[2] = atof(papszOffsetTokens[1]);
1387 0 : adfGeoTransform[3] = atof(papszOriginTokens[1]);
1388 0 : adfGeoTransform[4] = atof(papszOffsetTokens[3]);
1389 0 : adfGeoTransform[5] = atof(papszOffsetTokens[4]);
1390 : }
1391 : else
1392 : {
1393 : CPLError( CE_Failure, CPLE_AppDefined,
1394 : "2dGridIn3dCrs does not have expected GridOrigin or\n"
1395 0 : "GridOffsets values - unable to process WCS coverage.");
1396 0 : return FALSE;
1397 : }
1398 : }
1399 :
1400 0 : else if( strstr(pszGridType,":2dSimpleGrid") )
1401 : {
1402 0 : if( CSLCount(papszOffsetTokens) == 2
1403 : && CSLCount(papszOriginTokens) == 2 )
1404 : {
1405 0 : adfGeoTransform[0] = atof(papszOriginTokens[0]);
1406 0 : adfGeoTransform[1] = atof(papszOffsetTokens[0]);
1407 0 : adfGeoTransform[2] = 0.0;
1408 0 : adfGeoTransform[3] = atof(papszOriginTokens[1]);
1409 0 : adfGeoTransform[4] = 0.0;
1410 0 : adfGeoTransform[5] = atof(papszOffsetTokens[1]);
1411 : }
1412 : else
1413 : {
1414 : CPLError( CE_Failure, CPLE_AppDefined,
1415 : "2dSimpleGrid does not have expected GridOrigin or\n"
1416 0 : "GridOffsets values - unable to process WCS coverage.");
1417 0 : return FALSE;
1418 : }
1419 : }
1420 :
1421 : else
1422 : {
1423 : CPLError( CE_Failure, CPLE_AppDefined,
1424 : "Unrecognised GridCRS.GridType value '%s',\n"
1425 : "unable to process WCS coverage.",
1426 0 : pszGridType );
1427 0 : return FALSE;
1428 : }
1429 :
1430 0 : CSLDestroy( papszOffsetTokens );
1431 0 : CSLDestroy( papszOriginTokens );
1432 :
1433 : // GridOrigin is center of pixel ... offset half pixel to adjust.
1434 :
1435 0 : adfGeoTransform[0] -= (adfGeoTransform[1]+adfGeoTransform[2]) * 0.5;
1436 0 : adfGeoTransform[3] -= (adfGeoTransform[4]+adfGeoTransform[5]) * 0.5;
1437 :
1438 : /* -------------------------------------------------------------------- */
1439 : /* Establish our coordinate system. */
1440 : /* -------------------------------------------------------------------- */
1441 0 : osCRS = CPLGetXMLValue( psGCRS, "GridBaseCRS", "" );
1442 :
1443 0 : if( strlen(osCRS) == 0 )
1444 : {
1445 : CPLError( CE_Failure, CPLE_AppDefined,
1446 0 : "Unable to find GridCRS.GridBaseCRS" );
1447 0 : return FALSE;
1448 : }
1449 0 : else if( strstr(osCRS,":imageCRS") )
1450 : {
1451 : // raw image.
1452 : }
1453 : else
1454 : {
1455 0 : OGRSpatialReference oSRS;
1456 0 : if( oSRS.importFromURN( osCRS ) == OGRERR_NONE )
1457 : {
1458 0 : CPLFree( pszProjection );
1459 0 : oSRS.exportToWkt( &pszProjection );
1460 : }
1461 : else
1462 : {
1463 : CPLError( CE_Failure, CPLE_AppDefined,
1464 : "Unable to interprete GridBaseCRS '%s'.",
1465 0 : osCRS.c_str() );
1466 0 : return FALSE;
1467 0 : }
1468 : }
1469 :
1470 : /* -------------------------------------------------------------------- */
1471 : /* Search for an ImageCRS for raster size. */
1472 : /* -------------------------------------------------------------------- */
1473 : CPLXMLNode *psNode;
1474 :
1475 0 : nRasterXSize = -1;
1476 0 : nRasterYSize = -1;
1477 0 : for( psNode = psSD->psChild;
1478 : psNode != NULL && nRasterXSize == -1;
1479 : psNode = psNode->psNext )
1480 : {
1481 0 : if( psNode->eType != CXT_Element
1482 : || !EQUAL(psNode->pszValue,"BoundingBox") )
1483 0 : continue;
1484 :
1485 : double dfLX, dfLY, dfUX, dfUY;
1486 0 : CPLString osBBCRS;
1487 :
1488 0 : if( ParseBoundingBox( psNode, osBBCRS, dfLX, dfLY, dfUX, dfUY )
1489 : && strstr(osBBCRS,":imageCRS")
1490 : && dfLX == 0 && dfLY == 0 )
1491 : {
1492 0 : nRasterXSize = (int) (dfUX + 1.01);
1493 0 : nRasterYSize = (int) (dfUY + 1.01);
1494 : }
1495 : }
1496 :
1497 : /* -------------------------------------------------------------------- */
1498 : /* Otherwise we search for a bounding box in our coordinate */
1499 : /* system and derive the size from that. */
1500 : /* -------------------------------------------------------------------- */
1501 0 : for( psNode = psSD->psChild;
1502 : psNode != NULL && nRasterXSize == -1;
1503 : psNode = psNode->psNext )
1504 : {
1505 0 : if( psNode->eType != CXT_Element
1506 : || !EQUAL(psNode->pszValue,"BoundingBox") )
1507 0 : continue;
1508 :
1509 : double dfLX, dfLY, dfUX, dfUY;
1510 0 : CPLString osBBCRS;
1511 :
1512 0 : if( ParseBoundingBox( psNode, osBBCRS, dfLX, dfLY, dfUX, dfUY )
1513 : && osBBCRS == osCRS
1514 0 : && adfGeoTransform[2] == 0.0
1515 0 : && adfGeoTransform[4] == 0.0 )
1516 : {
1517 : nRasterXSize =
1518 0 : (int) ((dfUX - dfLX) / adfGeoTransform[1] + 1.01);
1519 : nRasterYSize =
1520 0 : (int) ((dfUY - dfLY) / fabs(adfGeoTransform[5]) + 1.01);
1521 : }
1522 : }
1523 :
1524 : /* -------------------------------------------------------------------- */
1525 : /* Do we have a coordinate system override? */
1526 : /* -------------------------------------------------------------------- */
1527 0 : const char *pszProjOverride = CPLGetXMLValue( psService, "SRS", NULL );
1528 :
1529 0 : if( pszProjOverride )
1530 : {
1531 0 : OGRSpatialReference oSRS;
1532 :
1533 0 : if( oSRS.SetFromUserInput( pszProjOverride ) != OGRERR_NONE )
1534 : {
1535 : CPLError( CE_Failure, CPLE_AppDefined,
1536 : "<SRS> element contents not parsable:\n%s",
1537 0 : pszProjOverride );
1538 0 : return FALSE;
1539 : }
1540 :
1541 0 : CPLFree( pszProjection );
1542 0 : oSRS.exportToWkt( &pszProjection );
1543 : }
1544 :
1545 : /* -------------------------------------------------------------------- */
1546 : /* Pick a format type if we don't already have one selected. */
1547 : /* */
1548 : /* We will prefer anything that sounds like TIFF, otherwise */
1549 : /* falling back to the first supported format. Should we */
1550 : /* consider preferring the nativeFormat if available? */
1551 : /* -------------------------------------------------------------------- */
1552 0 : if( CPLGetXMLValue( psService, "PreferredFormat", NULL ) == NULL )
1553 : {
1554 : CPLXMLNode *psNode;
1555 0 : CPLString osPreferredFormat;
1556 :
1557 0 : for( psNode = psCO->psChild; psNode != NULL; psNode = psNode->psNext )
1558 : {
1559 0 : if( psNode->eType == CXT_Element
1560 : && EQUAL(psNode->pszValue,"SupportedFormat")
1561 : && psNode->psChild
1562 : && psNode->psChild->eType == CXT_Text )
1563 : {
1564 0 : if( strlen(osPreferredFormat) == 0 )
1565 0 : osPreferredFormat = psNode->psChild->pszValue;
1566 :
1567 0 : if( strstr(psNode->psChild->pszValue,"tiff") != NULL
1568 : || strstr(psNode->psChild->pszValue,"TIFF") != NULL
1569 : || strstr(psNode->psChild->pszValue,"Tiff") != NULL )
1570 : {
1571 0 : osPreferredFormat = psNode->psChild->pszValue;
1572 0 : break;
1573 : }
1574 : }
1575 : }
1576 :
1577 0 : if( strlen(osPreferredFormat) > 0 )
1578 : {
1579 0 : bServiceDirty = TRUE;
1580 : CPLCreateXMLElementAndValue( psService, "PreferredFormat",
1581 0 : osPreferredFormat );
1582 0 : }
1583 : }
1584 :
1585 : /* -------------------------------------------------------------------- */
1586 : /* Try to identify a nodata value. For now we only support the */
1587 : /* singleValue mechanism. */
1588 : /* -------------------------------------------------------------------- */
1589 0 : if( CPLGetXMLValue( psService, "NoDataValue", NULL ) == NULL )
1590 : {
1591 : const char *pszSV =
1592 0 : CPLGetXMLValue( psCO, "Range.Field.NullValue", NULL );
1593 :
1594 0 : if( pszSV != NULL && (atof(pszSV) != 0.0 || *pszSV == '0') )
1595 : {
1596 0 : bServiceDirty = TRUE;
1597 : CPLCreateXMLElementAndValue( psService, "NoDataValue",
1598 0 : pszSV );
1599 : }
1600 : }
1601 :
1602 : /* -------------------------------------------------------------------- */
1603 : /* Grab the field name, if possible. */
1604 : /* -------------------------------------------------------------------- */
1605 0 : if( CPLGetXMLValue( psService, "FieldName", NULL ) == NULL )
1606 : {
1607 : CPLString osFieldName =
1608 0 : CPLGetXMLValue( psCO, "Range.Field.Identifier", "" );
1609 :
1610 0 : if( strlen(osFieldName) > 0 )
1611 : {
1612 0 : bServiceDirty = TRUE;
1613 : CPLCreateXMLElementAndValue( psService, "FieldName",
1614 0 : osFieldName );
1615 : }
1616 : else
1617 : {
1618 : CPLError( CE_Failure, CPLE_AppDefined,
1619 : "Unable to find required Identifier name %s for Range Field.",
1620 0 : osCRS.c_str() );
1621 0 : return FALSE;
1622 0 : }
1623 : }
1624 :
1625 : /* -------------------------------------------------------------------- */
1626 : /* Do we have a "Band" axis? If so try to grab the bandcount */
1627 : /* and data type from it. */
1628 : /* -------------------------------------------------------------------- */
1629 : CPLXMLNode * psAxis = CPLGetXMLNode(
1630 0 : psService, "CoverageDescription.Range.Field.Axis" );
1631 :
1632 0 : if( (EQUAL(CPLGetXMLValue(psAxis,"Identifier",""),"Band")
1633 : || EQUAL(CPLGetXMLValue(psAxis,"Identifier",""),"Bands"))
1634 : && CPLGetXMLNode(psAxis,"AvailableKeys") != NULL )
1635 : {
1636 0 : osBandIdentifier = CPLGetXMLValue(psAxis,"Identifier","");
1637 :
1638 : // verify keys are ascending starting at 1
1639 0 : CPLXMLNode *psValues = CPLGetXMLNode(psAxis,"AvailableKeys");
1640 : CPLXMLNode *psSV;
1641 : int iBand;
1642 :
1643 0 : for( psSV = psValues->psChild, iBand = 1;
1644 : psSV != NULL;
1645 : psSV = psSV->psNext, iBand++ )
1646 : {
1647 0 : if( psSV->eType != CXT_Element
1648 : || !EQUAL(psSV->pszValue,"Key")
1649 : || psSV->psChild == NULL
1650 : || psSV->psChild->eType != CXT_Text
1651 : || atoi(psSV->psChild->pszValue) != iBand )
1652 : {
1653 0 : osBandIdentifier = "";
1654 0 : break;
1655 : }
1656 : }
1657 :
1658 0 : if( strlen(osBandIdentifier) )
1659 : {
1660 0 : bServiceDirty = TRUE;
1661 0 : if( CPLGetXMLValue(psService,"BandIdentifier",NULL) == NULL )
1662 : CPLCreateXMLElementAndValue( psService, "BandIdentifier",
1663 0 : osBandIdentifier );
1664 :
1665 0 : if( CPLGetXMLValue(psService,"BandCount",NULL) == NULL )
1666 : CPLCreateXMLElementAndValue( psService, "BandCount",
1667 0 : CPLString().Printf("%d",iBand-1));
1668 : }
1669 :
1670 : // Is this an ESRI server returning a GDAL recognised data type?
1671 0 : CPLString osDataType = CPLGetXMLValue( psAxis, "DataType", "" );
1672 0 : if( GDALGetDataTypeByName(osDataType) != GDT_Unknown
1673 : && CPLGetXMLValue(psService,"BandType",NULL) == NULL )
1674 : {
1675 0 : bServiceDirty = TRUE;
1676 0 : CPLCreateXMLElementAndValue( psService, "BandType", osDataType );
1677 0 : }
1678 : }
1679 :
1680 0 : return TRUE;
1681 : }
1682 :
1683 : /************************************************************************/
1684 : /* ProcessError() */
1685 : /* */
1686 : /* Process an HTTP error, reporting it via CPL, and destroying */
1687 : /* the HTTP result object. Returns TRUE if there was an error, */
1688 : /* or FALSE if the result seems ok. */
1689 : /************************************************************************/
1690 :
1691 5 : int WCSDataset::ProcessError( CPLHTTPResult *psResult )
1692 :
1693 : {
1694 : /* -------------------------------------------------------------------- */
1695 : /* There isn't much we can do in this case. Hopefully an error */
1696 : /* was already issued by CPLHTTPFetch() */
1697 : /* -------------------------------------------------------------------- */
1698 5 : if( psResult == NULL || psResult->nDataLen == 0 )
1699 : {
1700 0 : CPLHTTPDestroyResult( psResult );
1701 0 : return TRUE;
1702 : }
1703 :
1704 : /* -------------------------------------------------------------------- */
1705 : /* If we got an html document, we presume it is an error */
1706 : /* message and report it verbatim up to a certain size limit. */
1707 : /* -------------------------------------------------------------------- */
1708 :
1709 5 : if( psResult->pszContentType != NULL
1710 : && strstr(psResult->pszContentType, "html") != NULL )
1711 : {
1712 0 : CPLString osErrorMsg = (char *) psResult->pabyData;
1713 :
1714 0 : if( osErrorMsg.size() > 2048 )
1715 0 : osErrorMsg.resize( 2048 );
1716 :
1717 : CPLError( CE_Failure, CPLE_AppDefined,
1718 : "Malformed Result:\n%s",
1719 0 : osErrorMsg.c_str() );
1720 0 : CPLHTTPDestroyResult( psResult );
1721 0 : return TRUE;
1722 : }
1723 :
1724 : /* -------------------------------------------------------------------- */
1725 : /* Does this look like a service exception? We would like to */
1726 : /* check based on the Content-type, but this seems quite */
1727 : /* undependable, even from MapServer! */
1728 : /* -------------------------------------------------------------------- */
1729 5 : if( strstr((const char *)psResult->pabyData, "ServiceException")
1730 : || strstr((const char *)psResult->pabyData, "ExceptionReport") )
1731 : {
1732 : CPLXMLNode *psTree = CPLParseXMLString( (const char *)
1733 0 : psResult->pabyData );
1734 0 : const char *pszMsg = NULL;
1735 :
1736 0 : CPLStripXMLNamespace( psTree, NULL, TRUE );
1737 :
1738 : // VERSION 1.0.0
1739 0 : if( psTree != NULL )
1740 : pszMsg = CPLGetXMLValue(psTree,
1741 : "=ServiceExceptionReport.ServiceException",
1742 0 : NULL );
1743 : // VERSION 1.1.0
1744 0 : if( pszMsg == NULL )
1745 : pszMsg = CPLGetXMLValue(psTree,
1746 : "=ExceptionReport.Exception.ExceptionText",
1747 0 : NULL );
1748 :
1749 0 : if( pszMsg )
1750 : CPLError( CE_Failure, CPLE_AppDefined,
1751 0 : "%s", pszMsg );
1752 : else
1753 : CPLError( CE_Failure, CPLE_AppDefined,
1754 : "Corrupt Service Exception:\n%s",
1755 0 : (const char *) psResult->pabyData );
1756 :
1757 0 : CPLDestroyXMLNode( psTree );
1758 0 : CPLHTTPDestroyResult( psResult );
1759 0 : return TRUE;
1760 : }
1761 :
1762 :
1763 : /* -------------------------------------------------------------------- */
1764 : /* Hopefully the error already issued by CPLHTTPFetch() is */
1765 : /* sufficient. */
1766 : /* -------------------------------------------------------------------- */
1767 5 : if( CPLGetLastErrorNo() != 0 )
1768 0 : return TRUE;
1769 :
1770 5 : return FALSE;
1771 : }
1772 :
1773 : /************************************************************************/
1774 : /* EstablishRasterDetails() */
1775 : /* */
1776 : /* Do a "test" coverage query to work out the number of bands, */
1777 : /* and pixel data type of the remote coverage. */
1778 : /************************************************************************/
1779 :
1780 2 : int WCSDataset::EstablishRasterDetails()
1781 :
1782 : {
1783 2 : CPLXMLNode * psCO = CPLGetXMLNode( psService, "CoverageOffering" );
1784 :
1785 2 : const char* pszCols = CPLGetXMLValue( psCO, "dimensionLimit.columns", NULL );
1786 2 : const char* pszRows = CPLGetXMLValue( psCO, "dimensionLimit.rows", NULL );
1787 2 : if( pszCols && pszRows )
1788 : {
1789 0 : nMaxCols = atoi(pszCols);
1790 0 : nMaxRows = atoi(pszRows);
1791 0 : SetMetadataItem("MAXNCOLS", pszCols, "IMAGE_STRUCTURE" );
1792 0 : SetMetadataItem("MAXNROWS", pszRows, "IMAGE_STRUCTURE" );
1793 : }
1794 :
1795 : /* -------------------------------------------------------------------- */
1796 : /* Do we already have bandcount and pixel type settings? */
1797 : /* -------------------------------------------------------------------- */
1798 2 : if( CPLGetXMLValue( psService, "BandCount", NULL ) != NULL
1799 : && CPLGetXMLValue( psService, "BandType", NULL ) != NULL )
1800 0 : return TRUE;
1801 :
1802 : /* -------------------------------------------------------------------- */
1803 : /* Fetch a small block of raster data. */
1804 : /* -------------------------------------------------------------------- */
1805 2 : CPLHTTPResult *psResult = NULL;
1806 : CPLErr eErr;
1807 :
1808 2 : eErr = GetCoverage( 0, 0, 2, 2, 2, 2, 0, NULL, &psResult );
1809 2 : if( eErr != CE_None )
1810 0 : return FALSE;
1811 :
1812 : /* -------------------------------------------------------------------- */
1813 : /* Try and open result as a dataseat. */
1814 : /* -------------------------------------------------------------------- */
1815 2 : GDALDataset *poDS = GDALOpenResult( psResult );
1816 :
1817 2 : if( poDS == NULL )
1818 0 : return FALSE;
1819 :
1820 2 : const char* pszPrj = poDS->GetProjectionRef();
1821 2 : if( pszPrj && strlen(pszPrj) > 0 )
1822 : {
1823 2 : if( pszProjection )
1824 2 : CPLFree( pszProjection );
1825 :
1826 2 : pszProjection = CPLStrdup( pszPrj );
1827 : }
1828 :
1829 : /* -------------------------------------------------------------------- */
1830 : /* Record details. */
1831 : /* -------------------------------------------------------------------- */
1832 2 : if( poDS->GetRasterCount() < 1 )
1833 : {
1834 0 : delete poDS;
1835 0 : return FALSE;
1836 : }
1837 :
1838 2 : if( CPLGetXMLValue(psService,"BandCount",NULL) == NULL )
1839 : CPLCreateXMLElementAndValue(
1840 : psService, "BandCount",
1841 2 : CPLString().Printf("%d",poDS->GetRasterCount()));
1842 :
1843 : CPLCreateXMLElementAndValue(
1844 : psService, "BandType",
1845 2 : GDALGetDataTypeName(poDS->GetRasterBand(1)->GetRasterDataType()) );
1846 :
1847 2 : bServiceDirty = TRUE;
1848 :
1849 : /* -------------------------------------------------------------------- */
1850 : /* Cleanup */
1851 : /* -------------------------------------------------------------------- */
1852 2 : delete poDS;
1853 :
1854 2 : FlushMemoryResult();
1855 :
1856 2 : return TRUE;
1857 : }
1858 :
1859 : /************************************************************************/
1860 : /* FlushMemoryResult() */
1861 : /* */
1862 : /* This actually either cleans up the in memory /vsimem/ */
1863 : /* temporary file, or the on disk temporary file. */
1864 : /************************************************************************/
1865 8 : void WCSDataset::FlushMemoryResult()
1866 :
1867 : {
1868 8 : if( strlen(osResultFilename) > 0 )
1869 : {
1870 3 : VSIUnlink( osResultFilename );
1871 3 : osResultFilename = "";
1872 : }
1873 :
1874 8 : if( pabySavedDataBuffer )
1875 : {
1876 3 : CPLFree( pabySavedDataBuffer );
1877 3 : pabySavedDataBuffer = NULL;
1878 : }
1879 8 : }
1880 :
1881 : /************************************************************************/
1882 : /* GDALOpenResult() */
1883 : /* */
1884 : /* Open a CPLHTTPResult as a GDALDataset (if possible). First */
1885 : /* attempt is to open handle it "in memory". Eventually we */
1886 : /* will add support for handling it on file if necessary. */
1887 : /* */
1888 : /* This method will free CPLHTTPResult, the caller should not */
1889 : /* access it after the call. */
1890 : /************************************************************************/
1891 :
1892 3 : GDALDataset *WCSDataset::GDALOpenResult( CPLHTTPResult *psResult )
1893 :
1894 : {
1895 3 : FlushMemoryResult();
1896 :
1897 : CPLDebug( "WCS", "GDALOpenResult() on content-type: %s",
1898 3 : psResult->pszContentType );
1899 :
1900 : /* -------------------------------------------------------------------- */
1901 : /* If this is multipart/related content type, we should search */
1902 : /* for the second part. */
1903 : /* -------------------------------------------------------------------- */
1904 3 : GByte *pabyData = psResult->pabyData;
1905 3 : int nDataLen = psResult->nDataLen;
1906 :
1907 3 : if( psResult->pszContentType
1908 : && strstr(psResult->pszContentType,"multipart")
1909 : && CPLHTTPParseMultipartMime(psResult) )
1910 : {
1911 0 : if( psResult->nMimePartCount > 1 )
1912 : {
1913 0 : pabyData = psResult->pasMimePart[1].pabyData;
1914 0 : nDataLen = psResult->pasMimePart[1].nDataLen;
1915 :
1916 0 : if (CSLFindString(psResult->pasMimePart[1].papszHeaders,
1917 : "Content-Transfer-Encoding: base64") != -1)
1918 : {
1919 0 : nDataLen = CPLBase64DecodeInPlace(pabyData);
1920 : }
1921 : }
1922 : }
1923 :
1924 : /* -------------------------------------------------------------------- */
1925 : /* Create a memory file from the result. */
1926 : /* -------------------------------------------------------------------- */
1927 : // Eventually we should be looking at mime info and stuff to figure
1928 : // out an optimal filename, but for now we just use a fixed one.
1929 : osResultFilename.Printf( "/vsimem/wcs/%p/wcsresult.dat",
1930 3 : this );
1931 :
1932 : VSILFILE *fp = VSIFileFromMemBuffer( osResultFilename, pabyData, nDataLen,
1933 3 : FALSE );
1934 :
1935 3 : if( fp == NULL )
1936 : {
1937 0 : CPLHTTPDestroyResult(psResult);
1938 0 : return NULL;
1939 : }
1940 :
1941 3 : VSIFCloseL( fp );
1942 :
1943 : /* -------------------------------------------------------------------- */
1944 : /* Try opening this result as a gdaldataset. */
1945 : /* -------------------------------------------------------------------- */
1946 : GDALDataset *poDS = (GDALDataset *)
1947 3 : GDALOpen( osResultFilename, GA_ReadOnly );
1948 :
1949 : /* -------------------------------------------------------------------- */
1950 : /* If opening it in memory didn't work, perhaps we need to */
1951 : /* write to a temp file on disk? */
1952 : /* -------------------------------------------------------------------- */
1953 3 : if( poDS == NULL )
1954 : {
1955 0 : CPLString osTempFilename;
1956 : VSILFILE *fpTemp;
1957 :
1958 0 : osTempFilename.Printf( "/tmp/%p_wcs.dat", this );
1959 :
1960 0 : fpTemp = VSIFOpenL( osTempFilename, "wb" );
1961 0 : if( fpTemp == NULL )
1962 : {
1963 : CPLError( CE_Failure, CPLE_OpenFailed,
1964 : "Failed to create temporary file:%s",
1965 0 : osTempFilename.c_str() );
1966 : }
1967 : else
1968 : {
1969 0 : if( VSIFWriteL( pabyData, nDataLen, 1, fpTemp )
1970 : != 1 )
1971 : {
1972 : CPLError( CE_Failure, CPLE_OpenFailed,
1973 : "Failed to write temporary file:%s",
1974 0 : osTempFilename.c_str() );
1975 0 : VSIFCloseL( fpTemp );
1976 0 : VSIUnlink( osTempFilename );
1977 : }
1978 : else
1979 : {
1980 0 : VSIFCloseL( fpTemp );
1981 0 : VSIUnlink( osResultFilename );
1982 0 : osResultFilename = osTempFilename;
1983 :
1984 : poDS = (GDALDataset *)
1985 0 : GDALOpen( osResultFilename, GA_ReadOnly );
1986 : }
1987 0 : }
1988 : }
1989 :
1990 : /* -------------------------------------------------------------------- */
1991 : /* Steal the memory buffer from HTTP result. */
1992 : /* -------------------------------------------------------------------- */
1993 3 : pabySavedDataBuffer = psResult->pabyData;
1994 :
1995 3 : psResult->pabyData = NULL;
1996 3 : psResult->nDataLen = psResult->nDataAlloc = 0;
1997 :
1998 3 : if( poDS == NULL )
1999 0 : FlushMemoryResult();
2000 :
2001 3 : CPLHTTPDestroyResult(psResult);
2002 :
2003 3 : return poDS;
2004 : }
2005 :
2006 : /************************************************************************/
2007 : /* Identify() */
2008 : /************************************************************************/
2009 :
2010 9245 : int WCSDataset::Identify( GDALOpenInfo * poOpenInfo )
2011 :
2012 : {
2013 : /* -------------------------------------------------------------------- */
2014 : /* Is this a WCS_GDAL service description file or "in url" */
2015 : /* equivelent? */
2016 : /* -------------------------------------------------------------------- */
2017 9245 : if( poOpenInfo->nHeaderBytes == 0
2018 : && EQUALN((const char *) poOpenInfo->pszFilename,"<WCS_GDAL>",10) )
2019 0 : return TRUE;
2020 :
2021 9245 : else if( poOpenInfo->nHeaderBytes >= 10
2022 : && EQUALN((const char *) poOpenInfo->pabyHeader,"<WCS_GDAL>",10) )
2023 0 : return TRUE;
2024 :
2025 : /* -------------------------------------------------------------------- */
2026 : /* Is this apparently a WCS subdataset reference? */
2027 : /* -------------------------------------------------------------------- */
2028 9245 : else if( EQUALN((const char *) poOpenInfo->pszFilename,"WCS_SDS:",8)
2029 : && poOpenInfo->nHeaderBytes == 0 )
2030 0 : return TRUE;
2031 :
2032 : else
2033 9245 : return FALSE;
2034 : }
2035 :
2036 : /************************************************************************/
2037 : /* Open() */
2038 : /************************************************************************/
2039 :
2040 2131 : GDALDataset *WCSDataset::Open( GDALOpenInfo * poOpenInfo )
2041 :
2042 : {
2043 2131 : char **papszModifiers = NULL;
2044 :
2045 : /* -------------------------------------------------------------------- */
2046 : /* Is this a WCS_GDAL service description file or "in url" */
2047 : /* equivelent? */
2048 : /* -------------------------------------------------------------------- */
2049 2131 : CPLXMLNode *psService = NULL;
2050 :
2051 2132 : if( poOpenInfo->nHeaderBytes == 0
2052 : && EQUALN((const char *) poOpenInfo->pszFilename,"<WCS_GDAL>",10) )
2053 : {
2054 1 : psService = CPLParseXMLString( poOpenInfo->pszFilename );
2055 : }
2056 2131 : else if( poOpenInfo->nHeaderBytes >= 10
2057 : && EQUALN((const char *) poOpenInfo->pabyHeader,"<WCS_GDAL>",10) )
2058 : {
2059 1 : psService = CPLParseXMLFile( poOpenInfo->pszFilename );
2060 : }
2061 : /* -------------------------------------------------------------------- */
2062 : /* Is this apparently a subdataset? */
2063 : /* -------------------------------------------------------------------- */
2064 2129 : else if( EQUALN((const char *) poOpenInfo->pszFilename,"WCS_SDS:",8)
2065 : && poOpenInfo->nHeaderBytes == 0 )
2066 : {
2067 : int iLast;
2068 :
2069 : papszModifiers = CSLTokenizeString2( poOpenInfo->pszFilename+8, ",",
2070 0 : CSLT_HONOURSTRINGS );
2071 :
2072 0 : iLast = CSLCount(papszModifiers)-1;
2073 0 : if( iLast >= 0 )
2074 : {
2075 0 : psService = CPLParseXMLFile( papszModifiers[iLast] );
2076 0 : CPLFree( papszModifiers[iLast] );
2077 0 : papszModifiers[iLast] = NULL;
2078 : }
2079 :
2080 : }
2081 :
2082 : /* -------------------------------------------------------------------- */
2083 : /* Success so far? */
2084 : /* -------------------------------------------------------------------- */
2085 2131 : if( psService == NULL )
2086 : {
2087 2129 : CSLDestroy( papszModifiers );
2088 2129 : return NULL;
2089 : }
2090 :
2091 : /* -------------------------------------------------------------------- */
2092 : /* Confirm the requested access is supported. */
2093 : /* -------------------------------------------------------------------- */
2094 2 : if( poOpenInfo->eAccess == GA_Update )
2095 : {
2096 0 : CSLDestroy( papszModifiers );
2097 0 : CPLDestroyXMLNode( psService );
2098 : CPLError( CE_Failure, CPLE_NotSupported,
2099 : "The WCS driver does not support update access to existing"
2100 0 : " datasets.\n" );
2101 0 : return NULL;
2102 : }
2103 :
2104 : /* -------------------------------------------------------------------- */
2105 : /* Check for required minimum fields. */
2106 : /* -------------------------------------------------------------------- */
2107 2 : if( !CPLGetXMLValue( psService, "ServiceURL", NULL )
2108 : || !CPLGetXMLValue( psService, "CoverageName", NULL ) )
2109 : {
2110 0 : CSLDestroy( papszModifiers );
2111 : CPLError( CE_Failure, CPLE_OpenFailed,
2112 : "Missing one or both of ServiceURL and CoverageName elements.\n"
2113 0 : "See WCS driver documentation for details on service description file format." );
2114 :
2115 0 : CPLDestroyXMLNode( psService );
2116 0 : return NULL;
2117 : }
2118 :
2119 : /* -------------------------------------------------------------------- */
2120 : /* What version are we working with? */
2121 : /* -------------------------------------------------------------------- */
2122 2 : const char *pszVersion = CPLGetXMLValue( psService, "Version", "1.0.0" );
2123 : int nVersion;
2124 :
2125 2 : if( EQUAL(pszVersion,"1.1.1") )
2126 0 : nVersion = 111;
2127 2 : else if( EQUAL(pszVersion,"1.1.0") )
2128 0 : nVersion = 110;
2129 2 : else if( EQUAL(pszVersion,"1.0.0") )
2130 2 : nVersion = 100;
2131 : else
2132 : {
2133 0 : CSLDestroy( papszModifiers );
2134 : CPLError( CE_Failure, CPLE_AppDefined,
2135 0 : "WCS Version '%s' not supported.", pszVersion );
2136 0 : CPLDestroyXMLNode( psService );
2137 0 : return NULL;
2138 : }
2139 :
2140 : /* -------------------------------------------------------------------- */
2141 : /* Create a corresponding GDALDataset. */
2142 : /* -------------------------------------------------------------------- */
2143 : WCSDataset *poDS;
2144 :
2145 2 : poDS = new WCSDataset();
2146 :
2147 2 : poDS->psService = psService;
2148 2 : poDS->SetDescription( poOpenInfo->pszFilename );
2149 2 : poDS->nVersion = nVersion;
2150 2 : poDS->papszSDSModifiers = papszModifiers;
2151 :
2152 : /* -------------------------------------------------------------------- */
2153 : /* Capture HTTP parameters. */
2154 : /* -------------------------------------------------------------------- */
2155 : const char *pszParm;
2156 :
2157 : poDS->papszHttpOptions =
2158 : CSLSetNameValue(poDS->papszHttpOptions,
2159 : "TIMEOUT",
2160 2 : CPLGetXMLValue( psService, "Timeout", "30" ) );
2161 :
2162 2 : pszParm = CPLGetXMLValue( psService, "HTTPAUTH", NULL );
2163 2 : if( pszParm )
2164 : poDS->papszHttpOptions =
2165 : CSLSetNameValue( poDS->papszHttpOptions,
2166 0 : "HTTPAUTH", pszParm );
2167 :
2168 2 : pszParm = CPLGetXMLValue( psService, "USERPWD", NULL );
2169 2 : if( pszParm )
2170 : poDS->papszHttpOptions =
2171 : CSLSetNameValue( poDS->papszHttpOptions,
2172 0 : "USERPWD", pszParm );
2173 :
2174 : /* -------------------------------------------------------------------- */
2175 : /* If we don't have the DescribeCoverage result for this */
2176 : /* coverage, fetch it now. */
2177 : /* -------------------------------------------------------------------- */
2178 2 : if( CPLGetXMLNode( psService, "CoverageOffering" ) == NULL
2179 : && CPLGetXMLNode( psService, "CoverageDescription" ) == NULL )
2180 : {
2181 2 : if( !poDS->DescribeCoverage() )
2182 : {
2183 0 : delete poDS;
2184 0 : return NULL;
2185 : }
2186 : }
2187 :
2188 : /* -------------------------------------------------------------------- */
2189 : /* Extract coordinate system, grid size, and geotransform from */
2190 : /* the coverage description and/or service description */
2191 : /* information. */
2192 : /* -------------------------------------------------------------------- */
2193 2 : if( !poDS->ExtractGridInfo() )
2194 : {
2195 0 : delete poDS;
2196 0 : return NULL;
2197 : }
2198 :
2199 2 : if( !poDS->EstablishRasterDetails() )
2200 : {
2201 0 : delete poDS;
2202 0 : return NULL;
2203 : }
2204 :
2205 : /* -------------------------------------------------------------------- */
2206 : /* Create band information objects. */
2207 : /* -------------------------------------------------------------------- */
2208 2 : int nBandCount = atoi(CPLGetXMLValue(psService,"BandCount","1"));
2209 : int iBand;
2210 :
2211 2 : if (!GDALCheckBandCount(nBandCount, 0))
2212 : {
2213 0 : delete poDS;
2214 0 : return NULL;
2215 : }
2216 :
2217 16 : for( iBand = 0; iBand < nBandCount; iBand++ )
2218 6 : poDS->SetBand( iBand+1, new WCSRasterBand( poDS, iBand+1, -1 ) );
2219 :
2220 : /* -------------------------------------------------------------------- */
2221 : /* Set time metadata on the dataset if we are selecting a */
2222 : /* temporal slice. */
2223 : /* -------------------------------------------------------------------- */
2224 : CPLString osTime = CSLFetchNameValueDef( poDS->papszSDSModifiers, "time",
2225 2 : poDS->osDefaultTime );
2226 :
2227 2 : if( osTime != "" )
2228 : poDS->GDALMajorObject::SetMetadataItem( "TIME_POSITION",
2229 0 : osTime.c_str() );
2230 :
2231 : /* -------------------------------------------------------------------- */
2232 : /* Do we have a band identifier to select only a subset of bands? */
2233 : /* -------------------------------------------------------------------- */
2234 2 : poDS->osBandIdentifier = CPLGetXMLValue(psService,"BandIdentifier","");
2235 :
2236 : /* -------------------------------------------------------------------- */
2237 : /* Do we have time based subdatasets? If so, record them in */
2238 : /* metadata. Note we don't do subdatasets if this is a */
2239 : /* subdataset or if this is an all-in-memory service. */
2240 : /* -------------------------------------------------------------------- */
2241 2 : if( !EQUALN(poOpenInfo->pszFilename,"WCS_SDS:",8)
2242 : && !EQUALN(poOpenInfo->pszFilename,"<WCS_GDAL>",10)
2243 : && poDS->aosTimePositions.size() > 0 )
2244 : {
2245 0 : char **papszSubdatasets = NULL;
2246 : int iTime;
2247 :
2248 0 : for( iTime = 0; iTime < (int)poDS->aosTimePositions.size(); iTime++ )
2249 : {
2250 0 : CPLString osName;
2251 0 : CPLString osValue;
2252 :
2253 0 : osName.Printf( "SUBDATASET_%d_NAME", iTime+1 );
2254 : osValue.Printf( "WCS_SDS:time=\"%s\",%s",
2255 : poDS->aosTimePositions[iTime].c_str(),
2256 0 : poOpenInfo->pszFilename );
2257 : papszSubdatasets = CSLSetNameValue( papszSubdatasets,
2258 0 : osName, osValue );
2259 :
2260 : CPLString osCoverage =
2261 0 : CPLGetXMLValue( poDS->psService, "CoverageName", "" );
2262 :
2263 0 : osName.Printf( "SUBDATASET_%d_DESC", iTime+1 );
2264 : osValue.Printf( "Coverage %s at time %s",
2265 : osCoverage.c_str(),
2266 0 : poDS->aosTimePositions[iTime].c_str() );
2267 : papszSubdatasets = CSLSetNameValue( papszSubdatasets,
2268 0 : osName, osValue );
2269 : }
2270 :
2271 : poDS->GDALMajorObject::SetMetadata( papszSubdatasets,
2272 0 : "SUBDATASETS" );
2273 :
2274 0 : CSLDestroy( papszSubdatasets );
2275 : }
2276 :
2277 : /* -------------------------------------------------------------------- */
2278 : /* Initialize any PAM information. */
2279 : /* -------------------------------------------------------------------- */
2280 2 : poDS->TryLoadXML();
2281 2 : return( poDS );
2282 : }
2283 :
2284 : /************************************************************************/
2285 : /* GetGeoTransform() */
2286 : /************************************************************************/
2287 :
2288 1 : CPLErr WCSDataset::GetGeoTransform( double * padfTransform )
2289 :
2290 : {
2291 1 : memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 );
2292 1 : return( CE_None );
2293 : }
2294 :
2295 : /************************************************************************/
2296 : /* GetProjectionRef() */
2297 : /************************************************************************/
2298 :
2299 1 : const char *WCSDataset::GetProjectionRef()
2300 :
2301 : {
2302 1 : const char* pszPrj = GDALPamDataset::GetProjectionRef();
2303 1 : if( pszPrj && strlen(pszPrj) > 0 )
2304 0 : return pszPrj;
2305 :
2306 1 : if ( pszProjection && strlen(pszProjection) > 0 )
2307 1 : return pszProjection;
2308 :
2309 0 : return( "" );
2310 : }
2311 :
2312 : /************************************************************************/
2313 : /* GetFileList() */
2314 : /************************************************************************/
2315 :
2316 0 : char **WCSDataset::GetFileList()
2317 :
2318 : {
2319 0 : char **papszFileList = GDALPamDataset::GetFileList();
2320 :
2321 : /* -------------------------------------------------------------------- */
2322 : /* ESRI also wishes to include service urls in the file list */
2323 : /* though this is not currently part of the general definition */
2324 : /* of GetFileList() for GDAL. */
2325 : /* -------------------------------------------------------------------- */
2326 : #ifdef ESRI_BUILD
2327 : CPLString file;
2328 : file.Printf( "%s%s",
2329 : CPLGetXMLValue( psService, "ServiceURL", "" ),
2330 : CPLGetXMLValue( psService, "CoverageName", "" ) );
2331 : papszFileList = CSLAddString( papszFileList, file.c_str() );
2332 : #endif /* def ESRI_BUILD */
2333 :
2334 0 : return papszFileList;
2335 : }
2336 :
2337 : /************************************************************************/
2338 : /* GetMetadata() */
2339 : /************************************************************************/
2340 :
2341 0 : char **WCSDataset::GetMetadata( const char *pszDomain )
2342 :
2343 : {
2344 0 : if( pszDomain == NULL
2345 : || !EQUAL(pszDomain,"xml:CoverageOffering") )
2346 0 : return GDALPamDataset::GetMetadata( pszDomain );
2347 :
2348 :
2349 0 : CPLXMLNode *psNode = CPLGetXMLNode( psService, "CoverageOffering" );
2350 :
2351 0 : if( psNode == NULL )
2352 0 : psNode = CPLGetXMLNode( psService, "CoverageDescription" );
2353 :
2354 0 : if( psNode == NULL )
2355 0 : return NULL;
2356 :
2357 0 : if( apszCoverageOfferingMD[0] == NULL )
2358 : {
2359 0 : CPLXMLNode *psNext = psNode->psNext;
2360 0 : psNode->psNext = NULL;
2361 :
2362 0 : apszCoverageOfferingMD[0] = CPLSerializeXMLTree( psNode );
2363 :
2364 0 : psNode->psNext = psNext;
2365 : }
2366 :
2367 0 : return apszCoverageOfferingMD;
2368 : }
2369 :
2370 :
2371 : /************************************************************************/
2372 : /* GDALRegister_WCS() */
2373 : /************************************************************************/
2374 :
2375 558 : void GDALRegister_WCS()
2376 :
2377 : {
2378 : GDALDriver *poDriver;
2379 :
2380 558 : if( GDALGetDriverByName( "WCS" ) == NULL )
2381 : {
2382 537 : poDriver = new GDALDriver();
2383 :
2384 537 : poDriver->SetDescription( "WCS" );
2385 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
2386 537 : "OGC Web Coverage Service" );
2387 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
2388 537 : "frmt_wcs.html" );
2389 537 : poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
2390 :
2391 537 : poDriver->pfnOpen = WCSDataset::Open;
2392 537 : poDriver->pfnIdentify = WCSDataset::Identify;
2393 :
2394 537 : GetGDALDriverManager()->RegisterDriver( poDriver );
2395 : }
2396 558 : }
|