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