1 /******************************************************************************
2  *
3  * Project:  WCS Client Driver
4  * Purpose:  Implementation of RasterBand classes for WCS.
5  * Author:   Frank Warmerdam, warmerdam@pobox.com
6  *
7  ******************************************************************************
8  * Copyright (c) 2006, Frank Warmerdam
9  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
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 "cpl_http.h"
31 #include "gdal_pam.h"
32 
33 #include <algorithm>
34 
35 #include "wcsdataset.h"
36 #include "wcsrasterband.h"
37 
38 /************************************************************************/
39 /*                           WCSRasterBand()                            */
40 /************************************************************************/
41 
WCSRasterBand(WCSDataset * poDSIn,int nBandIn,int iOverviewIn)42 WCSRasterBand::WCSRasterBand( WCSDataset *poDSIn, int nBandIn,
43                               int iOverviewIn ) :
44     iOverview(iOverviewIn),
45     nResFactor(1 << (iOverviewIn+1)), // iOverview == -1 is base layer
46     poODS(poDSIn),
47     nOverviewCount(0),
48     papoOverviews(nullptr)
49 {
50     poDS = poDSIn;
51     nBand = nBandIn;
52 
53     eDataType = GDALGetDataTypeByName(
54         CPLGetXMLValue( poDSIn->psService, "BandType", "Byte" ) );
55 
56 /* -------------------------------------------------------------------- */
57 /*      Establish resolution reduction for this overview level.         */
58 /* -------------------------------------------------------------------- */
59 
60 /* -------------------------------------------------------------------- */
61 /*      Establish block size.                                           */
62 /* -------------------------------------------------------------------- */
63     nRasterXSize = poDS->GetRasterXSize() / nResFactor;
64     nRasterYSize = poDS->GetRasterYSize() / nResFactor;
65 
66     nBlockXSize = atoi(CPLGetXMLValue( poDSIn->psService, "BlockXSize", "0" ) );
67     nBlockYSize = atoi(CPLGetXMLValue( poDSIn->psService, "BlockYSize", "0" ) );
68 
69     if( nBlockXSize < 1 )
70     {
71         if( nRasterXSize > 1800 )
72             nBlockXSize = 1024;
73         else
74             nBlockXSize = nRasterXSize;
75     }
76 
77     if( nBlockYSize < 1 )
78     {
79         if( nRasterYSize > 900 )
80             nBlockYSize = 512;
81         else
82             nBlockYSize = nRasterYSize;
83     }
84 
85 /* -------------------------------------------------------------------- */
86 /*      If this is the base layer, create the overview layers.          */
87 /* -------------------------------------------------------------------- */
88     if( iOverview == -1 )
89     {
90         nOverviewCount = atoi(CPLGetXMLValue(poODS->psService,"OverviewCount",
91                                              "-1"));
92         if( nOverviewCount < 0 )
93         {
94             for( nOverviewCount = 0;
95                  (std::max(nRasterXSize, nRasterYSize) /
96                   (1 << nOverviewCount)) > 900;
97                  nOverviewCount++ ) {}
98         }
99         else if( nOverviewCount > 30 )
100         {
101             /* There's no reason to have more than 30 overviews, because */
102             /* 2^(30+1) overflows a int32 */
103             nOverviewCount = 30;
104         }
105 
106         papoOverviews = (WCSRasterBand **)
107             CPLCalloc( nOverviewCount, sizeof(void*) );
108 
109         for( int i = 0; i < nOverviewCount; i++ )
110             papoOverviews[i] = new WCSRasterBand( poODS, nBand, i );
111     }
112 }
113 
114 /************************************************************************/
115 /*                           ~WCSRasterBand()                           */
116 /************************************************************************/
117 
~WCSRasterBand()118 WCSRasterBand::~WCSRasterBand()
119 
120 {
121     FlushCache();
122 
123     if( nOverviewCount > 0 )
124     {
125         for( int i = 0; i < nOverviewCount; i++ )
126             delete papoOverviews[i];
127 
128         CPLFree( papoOverviews );
129     }
130 }
131 
132 /************************************************************************/
133 /*                             IReadBlock()                             */
134 /************************************************************************/
135 
IReadBlock(int nBlockXOff,int nBlockYOff,void * pImage)136 CPLErr WCSRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
137                                   void * pImage )
138 
139 {
140     CPLErr eErr;
141     CPLHTTPResult *psResult = nullptr;
142 
143     // if INTERLEAVE is set to PIXEL, then we'll request all bands.
144     // That is necessary at least with MapServer, which seems to often
145     // return all bands instead of requested.
146     // todo: in 2.0.1 the band list in this dataset may be user-defined
147 
148     int band_count = 1;
149     if (EQUAL(CPLGetXMLValue(poODS->psService, "INTERLEAVE", ""), "PIXEL")) {
150         band_count = 0;
151     }
152 
153     eErr = poODS->GetCoverage( nBlockXOff * nBlockXSize * nResFactor,
154                                nBlockYOff * nBlockYSize * nResFactor,
155                                nBlockXSize * nResFactor,
156                                nBlockYSize * nResFactor,
157                                nBlockXSize, nBlockYSize,
158                                band_count, &nBand, nullptr, &psResult );
159     if( eErr != CE_None )
160         return eErr;
161 
162 /* -------------------------------------------------------------------- */
163 /*      Try and open result as a dataset.                               */
164 /* -------------------------------------------------------------------- */
165     GDALDataset *poTileDS = poODS->GDALOpenResult( psResult );
166 
167     if( poTileDS == nullptr )
168         return CE_Failure;
169 
170 /* -------------------------------------------------------------------- */
171 /*      Verify configuration.                                           */
172 /* -------------------------------------------------------------------- */
173     if( poTileDS->GetRasterXSize() != nBlockXSize
174         || poTileDS->GetRasterYSize() != nBlockYSize )
175     {
176         CPLError( CE_Failure, CPLE_AppDefined,
177                   "Returned tile does not match expected configuration.\n"
178                   "Got %dx%d instead of %dx%d.",
179                   poTileDS->GetRasterXSize(), poTileDS->GetRasterYSize(),
180                   nBlockXSize, nBlockYSize );
181         delete poTileDS;
182         return CE_Failure;
183     }
184 
185     if( band_count == 1
186         && ((strlen(poODS->osBandIdentifier) && poTileDS->GetRasterCount() != 1)
187             || (!strlen(poODS->osBandIdentifier)
188                 && poTileDS->GetRasterCount() != poODS->GetRasterCount())) )
189     {
190         CPLString msg;
191         if (strlen(poODS->osBandIdentifier) && poTileDS->GetRasterCount() != 1)
192         {
193             msg.Printf("Got %d bands instead of one although the coverage has band range type.\n",
194                        poTileDS->GetRasterCount());
195         } else {
196             msg.Printf("Response has %d bands while this dataset has %d bands.\n",
197                        poTileDS->GetRasterCount(), poODS->GetRasterCount());
198         }
199         CPLError( CE_Failure, CPLE_AppDefined,
200                   "Returned tile does not match expected band configuration.\n%s", msg.c_str());
201         delete poTileDS;
202         return CE_Failure;
203     }
204 
205 /* -------------------------------------------------------------------- */
206 /*      Process all bands of memory result, copying into pBuffer, or    */
207 /*      pushing into cache for other bands.                             */
208 /* -------------------------------------------------------------------- */
209     int iBand;
210     eErr = CE_None;
211 
212     for( iBand = 0;
213          iBand < poTileDS->GetRasterCount() && eErr == CE_None;
214          iBand++ )
215     {
216         GDALRasterBand *poTileBand = poTileDS->GetRasterBand( iBand+1 );
217 
218         if( iBand+1 == GetBand() || (band_count == 1 && strlen(poODS->osBandIdentifier)) )
219         {
220             eErr = poTileBand->RasterIO( GF_Read,
221                                          0, 0, nBlockXSize, nBlockYSize,
222                                          pImage, nBlockXSize, nBlockYSize,
223                                          eDataType, 0, 0, nullptr );
224         }
225         else
226         {
227             GDALRasterBand *poTargBand = poODS->GetRasterBand( iBand+1 );
228 
229             if( iOverview != -1 )
230                 poTargBand = poTargBand->GetOverview( iOverview );
231 
232             GDALRasterBlock *poBlock = poTargBand->GetLockedBlockRef(
233                 nBlockXOff, nBlockYOff, TRUE );
234 
235             if( poBlock != nullptr )
236             {
237                 eErr = poTileBand->RasterIO( GF_Read,
238                                             0, 0, nBlockXSize, nBlockYSize,
239                                             poBlock->GetDataRef(),
240                                             nBlockXSize, nBlockYSize,
241                                             eDataType, 0, 0, nullptr );
242                 poBlock->DropLock();
243             }
244             else
245                 eErr = CE_Failure;
246         }
247     }
248 
249 /* -------------------------------------------------------------------- */
250 /*      Cleanup                                                         */
251 /* -------------------------------------------------------------------- */
252     delete poTileDS;
253 
254     poODS->FlushMemoryResult();
255 
256     return eErr;
257 }
258 
259 /************************************************************************/
260 /*                             IRasterIO()                              */
261 /************************************************************************/
262 
IRasterIO(GDALRWFlag eRWFlag,int nXOff,int nYOff,int nXSize,int nYSize,void * pData,int nBufXSize,int nBufYSize,GDALDataType eBufType,GSpacing nPixelSpace,GSpacing nLineSpace,GDALRasterIOExtraArg * psExtraArg)263 CPLErr WCSRasterBand::IRasterIO( GDALRWFlag eRWFlag,
264                                  int nXOff, int nYOff, int nXSize, int nYSize,
265                                  void * pData, int nBufXSize, int nBufYSize,
266                                  GDALDataType eBufType,
267                                  GSpacing nPixelSpace, GSpacing nLineSpace,
268                                  GDALRasterIOExtraArg* psExtraArg)
269 
270 {
271     if( (poODS->nMaxCols > 0 && poODS->nMaxCols < nBufXSize)
272         ||  (poODS->nMaxRows > 0 && poODS->nMaxRows < nBufYSize) )
273         return CE_Failure;
274 
275     if( poODS->TestUseBlockIO( nXOff, nYOff, nXSize, nYSize,
276                                nBufXSize,nBufYSize ) )
277         return GDALPamRasterBand::IRasterIO(
278             eRWFlag, nXOff, nYOff, nXSize, nYSize,
279             pData, nBufXSize, nBufYSize, eBufType,
280             nPixelSpace, nLineSpace, psExtraArg );
281     else
282         return poODS->DirectRasterIO(
283             eRWFlag,
284             nXOff * nResFactor, nYOff * nResFactor,
285             nXSize * nResFactor, nYSize * nResFactor,
286             pData, nBufXSize, nBufYSize, eBufType,
287             1, &nBand, nPixelSpace, nLineSpace, 0, psExtraArg );
288 }
289 
290 /************************************************************************/
291 /*                           GetNoDataValue()                           */
292 /************************************************************************/
293 
GetNoDataValue(int * pbSuccess)294 double WCSRasterBand::GetNoDataValue( int *pbSuccess )
295 
296 {
297     const char *pszSV = CPLGetXMLValue( poODS->psService, "NoDataValue", nullptr);
298 
299     if( pszSV == nullptr )
300         return GDALPamRasterBand::GetNoDataValue( pbSuccess );
301     else
302     {
303         if( pbSuccess )
304             *pbSuccess = TRUE;
305         return CPLAtof(pszSV);
306     }
307 }
308 
309 /************************************************************************/
310 /*                          GetOverviewCount()                          */
311 /************************************************************************/
312 
GetOverviewCount()313 int WCSRasterBand::GetOverviewCount()
314 
315 {
316     return nOverviewCount;
317 }
318 
319 /************************************************************************/
320 /*                            GetOverview()                             */
321 /************************************************************************/
322 
GetOverview(int iOverviewIn)323 GDALRasterBand *WCSRasterBand::GetOverview( int iOverviewIn )
324 
325 {
326     if( iOverviewIn < 0 || iOverviewIn >= nOverviewCount )
327         return nullptr;
328     else
329         return papoOverviews[iOverviewIn];
330 }
331