1 /***********************************************************************
2  * File :    postgisrasterrasterband.cpp
3  * Project:  PostGIS Raster driver
4  * Purpose:  GDAL RasterBand implementation for PostGIS Raster driver
5  * Author:   Jorge Arevalo, jorge.arevalo@deimos-space.com
6  *                          jorgearevalo@libregis.org
7  *
8  * Author:	 David Zwarg, dzwarg@azavea.com
9  *
10  * Last changes: $Id: $
11  *
12  ***********************************************************************
13  * Copyright (c) 2009 - 2013, Jorge Arevalo, David Zwarg
14  * Copyright (c) 2013, Even Rouault
15  *
16  * Permission is hereby granted, free of charge, to any person obtaining
17  * a copy of this software and associated documentation files (the
18  * "Software"), to deal in the Software without restriction, including
19  * without limitation the rights to use, copy, modify, merge, publish,
20  * distribute, sublicense, and/or sell copies of the Software, and to
21  * permit persons to whom the Software is furnished to do so, subject to
22  * the following conditions:
23  *
24  * The above copyright notice and this permission notice shall be
25  * included in all copies or substantial portions of the Software.
26  *
27  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30  * NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
32  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
33  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
34  * SOFTWARE.
35  **********************************************************************/
36 #include "postgisraster.h"
37 
38 /**
39  * \brief Constructor.
40  *
41  * nBand it's just necessary for overview band creation
42  */
PostGISRasterRasterBand(PostGISRasterDataset * poDS,int nBand,GDALDataType eDataType,GBool bNoDataValueSet,double dfNodata,GBool bIsOffline=false)43 PostGISRasterRasterBand::PostGISRasterRasterBand(
44     PostGISRasterDataset * poDS, int nBand,
45     GDALDataType eDataType, GBool bNoDataValueSet, double dfNodata,
46     GBool bIsOffline = false) :
47     VRTSourcedRasterBand(poDS, nBand)
48 {
49     /* Basic properties */
50     this->poDS = poDS;
51     this->bIsOffline = bIsOffline;
52     this->nBand = nBand;
53 
54     this->eDataType = eDataType;
55     this->bNoDataValueSet = bNoDataValueSet;
56     this->dfNoDataValue = dfNodata;
57 
58     this->pszSchema = poDS->pszSchema;
59     this->pszTable = poDS->pszTable;
60     this->pszColumn = poDS->pszColumn;
61 
62     nRasterXSize = poDS->GetRasterXSize();
63     nRasterYSize = poDS->GetRasterYSize();
64 
65     /*******************************************************************
66      * Finally, set the block size. We apply the same logic than in VRT
67      * driver.
68      *
69      * We limit the size of a block with MAX_BLOCK_SIZE here to prevent
70      * arrangements of just one big tile.
71      *
72      * This value is just used in case whe only have 1 tile in the
73      * table. Otherwise, the reading operations are performed by the
74      * sources, not the PostGISRasterBand object itself.
75      ******************************************************************/
76     this->nBlockXSize = MIN(MAX_BLOCK_SIZE, this->nRasterXSize);
77     this->nBlockYSize = MIN(MAX_BLOCK_SIZE, this->nRasterYSize);
78 
79 #ifdef DEBUG_VERBOSE
80     CPLDebug("PostGIS_Raster",
81         "PostGISRasterRasterBand constructor: Band created (srid = %d)",
82         poDS->nSrid);
83 
84     CPLDebug("PostGIS_Raster",
85         "PostGISRasterRasterBand constructor: Band size: (%d X %d)",
86         nRasterXSize, nRasterYSize);
87 
88     CPLDebug("PostGIS_Raster", "PostGISRasterRasterBand::Constructor: "
89         "Block size (%dx%d)", this->nBlockXSize, this->nBlockYSize);
90 #endif
91 }
92 
93 /***********************************************
94  * \brief: Band destructor
95  ***********************************************/
~PostGISRasterRasterBand()96 PostGISRasterRasterBand::~PostGISRasterRasterBand()
97 {
98 }
99 
100 #ifdef notdef
101 /***********************************************************************
102  * \brief Set the block data to the null value if it is set, or zero if
103  * there is no null data value.
104  * Parameters:
105  *  - void *: the block data
106  * Returns: nothing
107  **********************************************************************/
NullBlock(void * pData)108 void PostGISRasterRasterBand::NullBlock(void *pData)
109 {
110     int nWords = nBlockXSize * nBlockYSize;
111     int nDataTypeSize = GDALGetDataTypeSize(eDataType) / 8;
112 
113     int bNoDataSet;
114     double dfNoData = GetNoDataValue(&bNoDataSet);
115     if (!bNoDataSet) {
116         memset(pData, 0, nWords * nDataTypeSize);
117     }
118 
119     else {
120         GDALCopyWords(&dfNoData, GDT_Float64, 0,
121                       pData, eDataType, nDataTypeSize,
122                       nWords);
123     }
124 }
125 
126 /***********************************************************************
127  * \brief Returns the metadata for this band
128  *
129  * If the metadata is actually stored in band's properties, simply
130  * returns them. Otherwise, it raises a query to fetch metadata
131  * FROM db
132  **********************************************************************/
GetBandMetadata(GDALDataType * peDataType,GBool * pbHasNoData,double * pdfNoData)133 GBool PostGISRasterRasterBand::GetBandMetadata(
134     GDALDataType * peDataType, GBool * pbHasNoData, double * pdfNoData)
135 {
136     // No need to raise a query
137     if (eDataType != GDT_Unknown) {
138         if (peDataType)
139             *peDataType = eDataType;
140 
141         if (pbHasNoData)
142             *pbHasNoData = bNoDataValueSet;
143 
144         if (pdfNoData)
145             *pdfNoData = dfNoDataValue;
146 
147         return true;
148     }
149 
150     /**
151      * Queries are expensive. So, we only raise them if all parameters
152      * are not null
153      **/
154     if (!peDataType || !pbHasNoData || !pdfNoData) {
155         return false;
156     }
157 
158     /**
159      * It is safe to assume all the tiles will have the same values for
160      * metadata properties. That was checked during band's construction
161      * (or we simply trusted the user, to avoid expensive checkings).
162      * So, we can limit the results to just one.
163      **/
164     int nTuples = 0;
165     CPLString osCommand = NULL;
166     PGresult * poResult = NULL;
167     PostGISRasterDataset * poRDS = (PostGISRasterDataset *)poDS;
168 
169     osCommand.Printf("st_bandpixeltype(%s, %d), "
170         "st_bandnodatavalue(%s, %d) is not null, "
171         "st_bandnodatavalue(%s, %d) FROM %s.%s limit 1", pszColumn,
172         nBand, pszColumn, nBand, pszColumn, nBand, pszSchema, pszTable);
173 
174 #ifdef DEBUG_QUERY
175     CPLDebug("PostGIS_Raster",
176         "PostGISRasterRasterBand::GetBandMetadata(): Query: %s",
177         osCommand.c_str());
178 #endif
179 
180     poResult = PQexec(poRDS->poConn, osCommand.c_str());
181     nTuples = PQntuples(poResult);
182 
183     /* Error getting info FROM database */
184     if (poResult == NULL ||
185         PQresultStatus(poResult) != PGRES_TUPLES_OK ||
186         nTuples <= 0) {
187 
188         ReportError(CE_Failure, CPLE_AppDefined,
189             "Error getting band metadata while creating raster "
190             "bands");
191 
192         CPLDebug("PostGIS_Raster",
193             "PostGISRasterDataset::GetBandMetadata(): %s",
194             PQerrorMessage(poRDS->poConn));
195 
196         if (poResult)
197             PQclear(poResult);
198 
199         return false;
200     }
201 
202     // Fill band metadata values
203     GBool bSignedByte = false;
204     int nBitsDepth = 8;
205     char* pszDataType = NULL;
206 
207     pszDataType = CPLStrdup(PQgetvalue(poResult, 0, 0));
208 
209     TranslateDataType(pszDataType, &eDataType, &nBitsDepth,
210             &bSignedByte);
211 
212     // Add pixeltype to image structure domain
213     if (bSignedByte) {
214         SetMetadataItem("PIXELTYPE", "SIGNEDBYTE", "IMAGE_STRUCTURE" );
215     }
216 
217     // Add NBITS to metadata only for sub-byte types
218     if (nBitsDepth < 8)
219         SetMetadataItem("NBITS", CPLString().Printf( "%d", nBitsDepth ),
220             "IMAGE_STRUCTURE" );
221 
222     CPLFree(pszDataType);
223 
224     bNoDataValueSet =
225             EQUALN(PQgetvalue(poResult, 0, 1), "t", sizeof(char));
226 
227     dfNoDataValue = CPLAtof(PQgetvalue(poResult, 0, 2));
228 
229     // Fill output arguments
230     *peDataType = eDataType;
231     *pbHasNoData = bNoDataValueSet;
232     *pdfNoData = dfNoDataValue;
233 
234     return true;
235 }
236 #endif
237 
238 /********************************************************
239  * \brief Set nodata value to a buffer
240  ********************************************************/
NullBuffer(void * pData,int nBufXSize,int nBufYSize,GDALDataType eBufType,int nPixelSpace,int nLineSpace)241 void PostGISRasterRasterBand::NullBuffer(void* pData,
242                                          int nBufXSize,
243                                          int nBufYSize,
244                                          GDALDataType eBufType,
245                                          int nPixelSpace,
246                                          int nLineSpace)
247 {
248     int j;
249     for(j = 0; j < nBufYSize; j++)
250     {
251         double dfVal = 0.0;
252         if( bNoDataValueSet )
253             dfVal = dfNoDataValue;
254         GDALCopyWords(&dfVal, GDT_Float64, 0,
255                     (GByte*)pData + j * nLineSpace, eBufType, nPixelSpace,
256                     nBufXSize);
257     }
258 }
259 
260 
261 /********************************************************
262  * \brief SortTilesByPKID
263  ********************************************************/
SortTilesByPKID(const void * a,const void * b)264 static int SortTilesByPKID(const void* a, const void* b)
265 {
266     PostGISRasterTileDataset* pa = *(PostGISRasterTileDataset** )a;
267     PostGISRasterTileDataset* pb = *(PostGISRasterTileDataset** )b;
268     return strcmp(pa->GetPKID(), pb->GetPKID());
269 }
270 
271 /**
272  * Read/write a region of image data for this band.
273  *
274  * This method allows reading a region of a PostGISRasterBand into a buffer.
275  * The write support is still under development
276  *
277  * The function fetches all the raster data that intersects with the region
278  * provided, and store the data in the GDAL cache.
279  *
280  * It automatically takes care of data type translation if the data type
281  * (eBufType) of the buffer is different than that of the PostGISRasterRasterBand.
282  *
283  * The nPixelSpace and nLineSpace parameters allow reading into FROM various
284  * organization of buffers.
285  *
286  * @param eRWFlag Either GF_Read to read a region of data (GF_Write, to write
287  * a region of data, yet not supported)
288  *
289  * @param nXOff The pixel offset to the top left corner of the region of the
290  * band to be accessed. This would be zero to start FROM the left side.
291  *
292  * @param nYOff The line offset to the top left corner of the region of the band
293  * to be accessed. This would be zero to start FROM the top.
294  *
295  * @param nXSize The width of the region of the band to be accessed in pixels.
296  *
297  * @param nYSize The height of the region of the band to be accessed in lines.
298  *
299  * @param pData The buffer into which the data should be read, or FROM which it
300  * should be written. This buffer must contain at least
301  * nBufXSize * nBufYSize * nBandCount words of type eBufType. It is organized in
302  * left to right,top to bottom pixel order. Spacing is controlled by the
303  * nPixelSpace, and nLineSpace parameters.
304  *
305  * @param nBufXSize the width of the buffer image into which the desired region
306  * is to be read, or FROM which it is to be written.
307  *
308  * @param nBufYSize the height of the buffer image into which the desired region
309  * is to be read, or FROM which it is to be written.
310  *
311  * @param eBufType the type of the pixel values in the pData data buffer. The
312  * pixel values will automatically be translated to/FROM the
313  * PostGISRasterRasterBand data type as needed.
314  *
315  * @param nPixelSpace The byte offset FROM the start of one pixel value in pData
316  * to the start of the next pixel value within a scanline. If defaulted (0) the
317  * size of the datatype eBufType is used.
318  *
319  * @param nLineSpace The byte offset FROM the start of one scanline in pData to
320  * the start of the next. If defaulted (0) the size of the datatype
321  * eBufType * nBufXSize is used.
322  *
323  * @return CE_Failure if the access fails, otherwise CE_None.
324  */
325 
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)326 CPLErr PostGISRasterRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
327     int nYOff, int nXSize, int nYSize, void * pData, int nBufXSize,
328     int nBufYSize, GDALDataType eBufType,
329     GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg* psExtraArg)
330 {
331     /**
332      * TODO: Write support not implemented yet
333      **/
334     if (eRWFlag == GF_Write) {
335         ReportError(CE_Failure, CPLE_NotSupported,
336             "Writing through PostGIS Raster band not supported yet");
337 
338         return CE_Failure;
339     }
340 
341     /*******************************************************************
342      * Do we have overviews that would be appropriate to satisfy this
343      * request?
344      ******************************************************************/
345     if( (nBufXSize < nXSize || nBufYSize < nYSize) &&
346         GetOverviewCount() > 0 )
347     {
348         if(OverviewRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
349             pData, nBufXSize, nBufYSize, eBufType, nPixelSpace,
350             nLineSpace, psExtraArg) == CE_None)
351 
352         return CE_None;
353     }
354 
355     PostGISRasterDataset * poRDS = (PostGISRasterDataset *)poDS;
356 
357     int bSameWindowAsOtherBand =
358         (nXOff == poRDS->nXOffPrev &&
359          nYOff == poRDS->nYOffPrev &&
360          nXSize == poRDS->nXSizePrev &&
361          nYSize == poRDS->nYSizePrev);
362     poRDS->nXOffPrev = nXOff;
363     poRDS->nYOffPrev = nYOff;
364     poRDS->nXSizePrev = nXSize;
365     poRDS->nYSizePrev = nYSize;
366 
367     /* Logic to determine if bands are read in order 1, 2, ... N */
368     /* If so, then use multi-band caching, otherwise do just single band caching */
369     if( poRDS->bAssumeMultiBandReadPattern )
370     {
371         if( nBand != poRDS->nNextExpectedBand )
372         {
373             CPLDebug("PostGIS_Raster",
374                     "Disabling multi-band caching since band access pattern does not match");
375             poRDS->bAssumeMultiBandReadPattern = false;
376             poRDS->nNextExpectedBand = 1;
377         }
378         else
379         {
380             poRDS->nNextExpectedBand ++;
381             if( poRDS->nNextExpectedBand > poRDS->GetRasterCount() )
382                 poRDS->nNextExpectedBand = 1;
383         }
384     }
385     else
386     {
387         if( nBand == poRDS->nNextExpectedBand )
388         {
389             poRDS->nNextExpectedBand ++;
390             if( poRDS->nNextExpectedBand > poRDS->GetRasterCount() )
391             {
392                 CPLDebug("PostGIS_Raster", "Re-enabling multi-band caching");
393                 poRDS->bAssumeMultiBandReadPattern = true;
394                 poRDS->nNextExpectedBand = 1;
395             }
396         }
397     }
398 
399 #ifdef DEBUG_VERBOSE
400     CPLDebug("PostGIS_Raster",
401             "PostGISRasterRasterBand::IRasterIO: "
402             "nBand = %d, nXOff = %d, nYOff = %d, nXSize = %d, nYSize = %d, nBufXSize = %d, nBufYSize = %d",
403              nBand, nXOff, nYOff, nXSize,  nYSize, nBufXSize, nBufYSize);
404 #endif
405 
406 #ifdef notdef
407     /*******************************************************************
408      * Optimization: We just have one tile. So, we can read it with
409      * IReadBlock
410      *
411      * TODO: Review it. It's not working (see comment in
412      * PostGISRasterDataset::ConstructOneDatasetFromTiles)
413      ******************************************************************/
414 
415     if (poRDS->nTiles == 1) {
416 
417         return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize,
418             nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace,
419             nLineSpace, psExtraArg);
420     }
421 #endif
422 
423     /*******************************************************************
424      * Several tiles: we first look in all our sources caches. Missing
425      * blocks are queried
426      ******************************************************************/
427     double adfProjWin[8];
428     int nFeatureCount = 0;
429     CPLRectObj sAoi;
430 
431     poRDS->PolygonFromCoords(nXOff, nYOff, nXOff + nXSize, nYOff + nYSize, adfProjWin);
432     // (p[6], p[7]) is the minimum (x, y), and (p[2], p[3]) the max
433     sAoi.minx = adfProjWin[6];
434     sAoi.maxx = adfProjWin[2];
435     if( adfProjWin[7] < adfProjWin[3] )
436     {
437         sAoi.miny = adfProjWin[7];
438         sAoi.maxy = adfProjWin[3];
439     }
440     else
441     {
442         sAoi.maxy = adfProjWin[7];
443         sAoi.miny = adfProjWin[3];
444     }
445 
446 #ifdef DEBUG_VERBOSE
447     CPLDebug("PostGIS_Raster",
448             "PostGISRasterRasterBand::IRasterIO: "
449             "Intersection box: (%f, %f) - (%f, %f)", sAoi.minx,
450             sAoi.miny, sAoi.maxx, sAoi.maxy);
451 #endif
452 
453     if (poRDS->hQuadTree == NULL)
454     {
455         ReportError(CE_Failure, CPLE_AppDefined,
456             "Could not read metadata index.");
457         return CE_Failure;
458     }
459 
460     NullBuffer(pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace);
461 
462     if( poRDS->bBuildQuadTreeDynamically && !bSameWindowAsOtherBand )
463     {
464         if( !(poRDS->LoadSources(nXOff, nYOff, nXSize, nYSize, nBand)) )
465             return CE_Failure;
466     }
467 
468     // Matching sources, to avoid a dumb for loop over the sources
469     PostGISRasterTileDataset ** papsMatchingTiles =
470         (PostGISRasterTileDataset **)
471             CPLQuadTreeSearch(poRDS->hQuadTree, &sAoi, &nFeatureCount);
472 
473     // No blocks found. This is not an error (the raster may have holes)
474     if (nFeatureCount == 0) {
475         CPLFree(papsMatchingTiles);
476 
477         return CE_None;
478     }
479 
480     int i;
481 
482     /**
483      * We need to store the max, min coords for the missing tiles in
484      * any place. This is as good as any other
485      **/
486     sAoi.minx = 0.0;
487     sAoi.miny = 0.0;
488     sAoi.maxx = 0.0;
489     sAoi.maxy = 0.0;
490 
491     GIntBig nMemoryRequiredForTiles = 0;
492     CPLString osIDsToFetch;
493     int nTilesToFetch = 0;
494     int nBandDataTypeSize = GDALGetDataTypeSize(eDataType) / 8;
495 
496     // Loop just over the intersecting sources
497     for(i = 0; i < nFeatureCount; i++) {
498         PostGISRasterTileDataset *poTile = papsMatchingTiles[i];
499         PostGISRasterTileRasterBand* poTileBand =
500             (PostGISRasterTileRasterBand *)poTile->GetRasterBand(nBand);
501 
502         nMemoryRequiredForTiles += poTileBand->GetXSize() * poTileBand->GetYSize() *
503             nBandDataTypeSize;
504 
505         // Missing tile: we'll need to query for it
506         if (!poTileBand->IsCached()) {
507 
508             // If we have a PKID, add the tile PKID to the list
509             if (poTile->pszPKID != NULL)
510             {
511                 if( osIDsToFetch.size() != 0 )
512                     osIDsToFetch += ",";
513                 osIDsToFetch += "'";
514                 osIDsToFetch += poTile->pszPKID;
515                 osIDsToFetch += "'";
516             }
517 
518             double dfTileMinX, dfTileMinY, dfTileMaxX, dfTileMaxY;
519             poTile->GetExtent(&dfTileMinX, &dfTileMinY,
520                               &dfTileMaxX, &dfTileMaxY);
521 
522             /**
523              * We keep the general max and min values of all the missing
524              * tiles, to raise a query that intersect just that area.
525              *
526              * TODO: In case of just a few tiles and very separated,
527              * this strategy is clearly suboptimal. We'll get our
528              * missing tiles, but with a lot of other not needed tiles.
529              *
530              * A possible optimization will be to simply rely on the
531              * I/O method of the source (must be implemented), in case
532              * we have minus than a reasonable amount of tiles missing.
533              * Another criteria to decide would be how separated the
534              * tiles are. Two queries for just two adjacent tiles is
535              * also a dumb strategy.
536              **/
537             if( nTilesToFetch == 0 )
538             {
539                 sAoi.minx = dfTileMinX;
540                 sAoi.miny = dfTileMinY;
541                 sAoi.maxx = dfTileMaxX;
542                 sAoi.maxy = dfTileMaxY;
543             }
544             else
545             {
546                 if (dfTileMinX < sAoi.minx)
547                     sAoi.minx = dfTileMinX;
548 
549                 if (dfTileMinY < sAoi.miny)
550                     sAoi.miny = dfTileMinY;
551 
552                 if (dfTileMaxX > sAoi.maxx)
553                     sAoi.maxx = dfTileMaxX;
554 
555                 if (dfTileMaxY > sAoi.maxy)
556                     sAoi.maxy = dfTileMaxY;
557             }
558 
559             nTilesToFetch ++;
560 
561         }
562     }
563 
564     /* Determine caching strategy */
565     int bAllBandCaching = FALSE;
566     if (nTilesToFetch > 0)
567     {
568         GIntBig nCacheMax = (GIntBig) GDALGetCacheMax64();
569         if( nMemoryRequiredForTiles > nCacheMax )
570         {
571             CPLDebug("PostGIS_Raster",
572                     "For best performance, the block cache should be able to store " CPL_FRMT_GIB
573                     " bytes for the tiles of the requested window, "
574                     "but it is only " CPL_FRMT_GIB " byte large",
575                     nMemoryRequiredForTiles, nCacheMax );
576             nTilesToFetch = 0;
577         }
578 
579         if( poRDS->GetRasterCount() > 1 && poRDS->bAssumeMultiBandReadPattern )
580         {
581             GIntBig nMemoryRequiredForTilesAllBands =
582                 nMemoryRequiredForTiles * poRDS->GetRasterCount();
583             if( nMemoryRequiredForTilesAllBands <= nCacheMax )
584             {
585                 bAllBandCaching = TRUE;
586             }
587             else
588             {
589                 CPLDebug("PostGIS_Raster", "Caching only this band, but not all bands. "
590                          "Cache should be " CPL_FRMT_GIB " byte large for that",
591                          nMemoryRequiredForTilesAllBands);
592             }
593         }
594     }
595 
596     // Raise a query for missing tiles and cache them
597     if (nTilesToFetch > 0) {
598 
599         /**
600          * There are several options here, to raise the query.
601          * - Get all the tiles which PKID is in a list of missing
602          *   PKIDs.
603          * - Get all the tiles that intersect a polygon constructed
604          *   based on the (min - max) values calculated before.
605          * - Get all the tiles with upper left pixel included in the
606          *   range (min - max) calculated before.
607          *
608          * The first option is the most efficient one when a PKID exists.
609          * After that, the second one is the most efficient one when a
610          * spatial index exists.
611          * The third one is the only one available when neither a PKID or spatial
612          * index exist.
613          **/
614         CPLString osCommand;
615         PGresult * poResult;
616 
617         CPLString osRasterToFetch;
618         if (bAllBandCaching)
619             osRasterToFetch = pszColumn;
620         else
621             osRasterToFetch.Printf("ST_Band(%s, %d)", pszColumn, nBand);
622 
623         int bHasWhere = FALSE;
624         if (osIDsToFetch.size() && (poRDS->bIsFastPK || !(poRDS->HasSpatialIndex())) ) {
625             osCommand.Printf("SELECT %s, "
626                 "ST_Metadata(%s), %s FROM %s.%s",
627                 osRasterToFetch.c_str(), pszColumn,
628                 poRDS->GetPrimaryKeyRef(), pszSchema, pszTable);
629             if( nTilesToFetch < poRDS->nTiles || poRDS->bBuildQuadTreeDynamically )
630             {
631                 bHasWhere = TRUE;
632                 osCommand += " WHERE ";
633                 osCommand += poRDS->pszPrimaryKeyName;
634                 osCommand += " IN (";
635                 osCommand += osIDsToFetch;
636                 osCommand += ")";
637             }
638         }
639 
640         else {
641             bHasWhere = TRUE;
642             osCommand.Printf("SELECT %s, ST_Metadata(%s), %s FROM %s.%s WHERE ",
643                              osRasterToFetch.c_str(), pszColumn,
644                              (poRDS->GetPrimaryKeyRef()) ? poRDS->GetPrimaryKeyRef() : "'foo'",
645                              pszSchema, pszTable);
646             if( poRDS->HasSpatialIndex() )
647             {
648                 osCommand += CPLSPrintf("%s && "
649                         "ST_GeomFromText('POLYGON((%.18f %.18f,%.18f %.18f,%.18f %.18f,%.18f %.18f,%.18f %.18f))')",
650                         pszColumn,
651                         adfProjWin[0], adfProjWin[1],
652                         adfProjWin[2], adfProjWin[3],
653                         adfProjWin[4], adfProjWin[5],
654                         adfProjWin[6], adfProjWin[7],
655                         adfProjWin[0], adfProjWin[1]);
656             }
657             else
658             {
659                 #define EPS 1e-5
660                 osCommand += CPLSPrintf("ST_UpperLeftX(%s)"
661                     " BETWEEN %f AND %f AND ST_UpperLeftY(%s) BETWEEN "
662                     "%f AND %f", pszColumn, sAoi.minx-EPS, sAoi.maxx+EPS,
663                     pszColumn, sAoi.miny-EPS, sAoi.maxy+EPS);
664             }
665         }
666 
667         if( poRDS->pszWhere != NULL )
668         {
669             if( bHasWhere )
670                 osCommand += " AND (";
671             else
672                 osCommand += " WHERE (";
673             osCommand += poRDS->pszWhere;
674             osCommand += ")";
675         }
676 
677         poResult = PQexec(poRDS->poConn, osCommand.c_str());
678 
679 #ifdef DEBUG_QUERY
680         CPLDebug("PostGIS_Raster",
681             "PostGISRasterRasterBand::IRasterIO(): Query = \"%s\" --> number of rows = %d",
682             osCommand.c_str(), poResult ? PQntuples(poResult) : 0 );
683 #endif
684 
685         if (poResult == NULL ||
686             PQresultStatus(poResult) != PGRES_TUPLES_OK ||
687             PQntuples(poResult) < 0) {
688 
689             if (poResult)
690                 PQclear(poResult);
691 
692             CPLError(CE_Failure, CPLE_AppDefined,
693                 "PostGISRasterRasterBand::IRasterIO(): %s",
694                 PQerrorMessage(poRDS->poConn));
695 
696             // Free the object that holds pointers to matching tiles
697             CPLFree(papsMatchingTiles);
698             return CE_Failure;
699         }
700 
701         /**
702          * No data. Return the buffer filled with nodata values
703          **/
704         else if (PQntuples(poResult) == 0) {
705             PQclear(poResult);
706 
707             // Free the object that holds pointers to matching tiles
708             CPLFree(papsMatchingTiles);
709             return CE_None;
710         }
711 
712         /**
713          * Ok, we loop over the results
714          **/
715         int nTuples = PQntuples(poResult);
716         for(i = 0; i < nTuples; i++)
717         {
718             const char* pszMetadata = PQgetvalue(poResult, i, 1);
719             const char* pszRaster = PQgetvalue(poResult, i, 0);
720             const char *pszPKID = (poRDS->GetPrimaryKeyRef() != NULL) ?  PQgetvalue(poResult, i, 2) : NULL;
721             poRDS->CacheTile(pszMetadata, pszRaster, pszPKID, nBand, bAllBandCaching);
722         } // All tiles have been added to cache
723 
724         PQclear(poResult);
725     } // End missing tiles
726 
727 /* -------------------------------------------------------------------- */
728 /*      Overlay each source in turn over top this.                      */
729 /* -------------------------------------------------------------------- */
730 
731     CPLErr eErr = CE_None;
732     /* Sort tiles by ascending PKID, so that the draw order is determinist */
733     if( poRDS->GetPrimaryKeyRef() != NULL )
734     {
735         qsort(papsMatchingTiles, nFeatureCount, sizeof(PostGISRasterTileDataset*),
736               SortTilesByPKID);
737     }
738 
739     for(i = 0; i < nFeatureCount && eErr == CE_None; i++)
740     {
741         PostGISRasterTileDataset *poTile = papsMatchingTiles[i];
742         PostGISRasterTileRasterBand* poTileBand =
743             (PostGISRasterTileRasterBand *)poTile->GetRasterBand(nBand);
744         eErr =
745             poTileBand->poSource->RasterIO( nXOff, nYOff, nXSize, nYSize,
746                                             pData, nBufXSize, nBufYSize,
747                                             eBufType, nPixelSpace, nLineSpace, NULL);
748     }
749 
750     // Free the object that holds pointers to matching tiles
751     CPLFree(papsMatchingTiles);
752 
753     return eErr;
754 }
755 
756 /**
757  * \brief Set the no data value for this band.
758  * Parameters:
759  *  - double: The nodata value
760  * Returns:
761  *  - CE_None.
762  */
SetNoDataValue(double dfNewValue)763 CPLErr PostGISRasterRasterBand::SetNoDataValue(double dfNewValue) {
764     dfNoDataValue = dfNewValue;
765 
766     return CE_None;
767 }
768 
769 /**
770  * \brief Fetch the no data value for this band.
771  * Parameters:
772  *  - int *: pointer to a boolean to use to indicate if a value is actually
773  *          associated with this layer. May be NULL (default).
774  *  Returns:
775  *  - double: the nodata value for this band.
776  */
GetNoDataValue(int * pbSuccess)777 double PostGISRasterRasterBand::GetNoDataValue(int *pbSuccess) {
778     if (pbSuccess != NULL)
779         *pbSuccess = (int) bNoDataValueSet;
780 
781     return dfNoDataValue;
782 }
783 
784 /***************************************************
785  * \brief Return the number of overview layers available
786  ***************************************************/
GetOverviewCount()787 int PostGISRasterRasterBand::GetOverviewCount()
788 {
789     PostGISRasterDataset * poRDS = (PostGISRasterDataset *)poDS;
790     return poRDS->GetOverviewCount();
791 }
792 
793 /**********************************************************
794  * \brief Fetch overview raster band object
795  **********************************************************/
GetOverview(int i)796 GDALRasterBand * PostGISRasterRasterBand::GetOverview(int i)
797 {
798     if (i < 0 || i >= GetOverviewCount())
799         return NULL;
800 
801     PostGISRasterDataset* poRDS = (PostGISRasterDataset *)poDS;
802     PostGISRasterDataset* poOverviewDS = poRDS->GetOverviewDS(i);
803     if( poOverviewDS->nBands == 0 )
804     {
805         if (!poOverviewDS->SetRasterProperties(NULL) ||
806              poOverviewDS->GetRasterCount() != poRDS->GetRasterCount())
807         {
808             CPLDebug("PostGIS_Raster",
809                      "Request for overview %d of band %d failed", i, nBand);
810             return NULL;
811         }
812     }
813 
814     return poOverviewDS->GetRasterBand(nBand);
815 }
816 
817 #ifdef notdef
818 /*****************************************************
819  * \brief Read a natural block of raster band data
820  *****************************************************/
IReadBlock(int nBlockXOff,int nBlockYOff,void * pImage)821 CPLErr PostGISRasterRasterBand::IReadBlock(int nBlockXOff,
822         int nBlockYOff, void * pImage)
823 {
824     PGresult * poResult = NULL;
825     CPLString osCommand;
826     int nXOff = 0;
827     int nYOff = 0;
828     int nNaturalBlockXSize = 0;
829     int nNaturalBlockYSize = 0;
830     double adfProjWin[8];
831     PostGISRasterDataset * poRDS = (PostGISRasterDataset *)poDS;
832 
833     int nPixelSize = GDALGetDataTypeSize(eDataType) / 8;
834 
835     // Construct a polygon to intersect with
836     GetBlockSize(&nNaturalBlockXSize, &nNaturalBlockYSize);
837 
838     nXOff = nBlockXOff * nNaturalBlockXSize;
839     nYOff = nBlockYOff * nNaturalBlockYSize;
840 
841     poRDS->PolygonFromCoords(nXOff, nYOff, nXOff + nNaturalBlockXSize, nYOff + nNaturalBlockYSize, adfProjWin);
842 
843     // Raise the query
844     if (poRDS->pszWhere == NULL) {
845         osCommand.Printf("SELECT st_band(%s, %d) FROM %s.%s "
846             "WHERE st_intersects(%s, ST_PolygonFromText"
847             "('POLYGON((%.17f %.17f, %.17f %.17f, %.17f %.17f, %.17f "
848             "%.17f, %.17f %.17f))', %d))", pszColumn, nBand, pszSchema,
849             pszTable, pszColumn, adfProjWin[0], adfProjWin[1],
850             adfProjWin[2], adfProjWin[3],  adfProjWin[4], adfProjWin[5],
851             adfProjWin[6], adfProjWin[7], adfProjWin[0], adfProjWin[1],
852             poRDS->nSrid);
853     }
854 
855     else {
856         osCommand.Printf("SELECT st_band(%s, %d) FROM %s.%s WHERE (%s) "
857             "AND st_intersects(%s, ST_PolygonFromText"
858             "('POLYGON((%.17f %.17f, %.17f %.17f, %.17f %.17f, %.17f "
859             "%.17f, %.17f %.17f))', %d))", pszColumn, nBand, pszSchema,
860             pszTable, poRDS->pszWhere, pszColumn, adfProjWin[0],
861             adfProjWin[1], adfProjWin[2], adfProjWin[3], adfProjWin[4],
862             adfProjWin[5], adfProjWin[6], adfProjWin[7], adfProjWin[0],
863             adfProjWin[1], poRDS->nSrid);
864     }
865 
866 #ifdef DEBUG_QUERY
867     CPLDebug("PostGIS_Raster",
868         "PostGISRasterRasterBand::IReadBlock(): Query = %s",
869             osCommand.c_str());
870 #endif
871 
872     poResult = PQexec(poRDS->poConn, osCommand.c_str());
873     if (poResult == NULL ||
874         PQresultStatus(poResult) != PGRES_TUPLES_OK ||
875         PQntuples(poResult) < 0) {
876 
877         if (poResult)
878             PQclear(poResult);
879 
880         ReportError(CE_Failure, CPLE_AppDefined,
881             "Error retrieving raster data FROM database");
882 
883         CPLDebug("PostGIS_Raster",
884             "PostGISRasterRasterBand::IRasterIO(): %s",
885                 PQerrorMessage(poRDS->poConn));
886 
887         return CE_Failure;
888     }
889 
890     /**
891      * No data. Return the buffer filled with nodata values
892      **/
893     else if (PQntuples(poResult) == 0) {
894         PQclear(poResult);
895 
896         CPLDebug("PostGIS_Raster",
897             "PostGISRasterRasterBand::IRasterIO(): Null block");
898 
899         NullBlock(pImage);
900 
901         return CE_None;
902     }
903 
904 
905     /**
906      * Ok, we get the data. Only data size, without payload
907      *
908      * TODO: Check byte order
909      **/
910     int nExpectedDataSize = nNaturalBlockXSize * nNaturalBlockYSize *
911         nPixelSize;
912 
913     int nWKBLength = 0;
914 
915     GByte * pbyData = CPLHexToBinary(PQgetvalue(poResult, 0, 0),
916         &nWKBLength);
917 
918     char * pbyDataToRead = (char*)GET_BAND_DATA(pbyData,nBand,
919             nPixelSize, nExpectedDataSize);
920 
921     memcpy(pImage, pbyDataToRead, nExpectedDataSize * sizeof(char));
922 
923     CPLDebug("PostGIS_Raster", "IReadBlock: Copied %d bytes FROM block "
924             "(%d, %d) to %p", nExpectedDataSize, nBlockXOff,
925             nBlockYOff, pImage);
926 
927     CPLFree(pbyData);
928     PQclear(poResult);
929 
930     return CE_None;
931 }
932 #endif
933 
934 /**
935  * \brief How should this band be interpreted as color?
936  * GCI_Undefined is returned when the format doesn't know anything about the
937  * color interpretation.
938  **/
GetColorInterpretation()939 GDALColorInterp PostGISRasterRasterBand::GetColorInterpretation()
940 {
941     if (poDS->GetRasterCount() == 1) {
942         eColorInterp = GCI_GrayIndex;
943     }
944 
945     else if (poDS->GetRasterCount() == 3) {
946         if (nBand == 1)
947             eColorInterp = GCI_RedBand;
948         else if( nBand == 2 )
949             eColorInterp = GCI_GreenBand;
950         else if( nBand == 3 )
951             eColorInterp = GCI_BlueBand;
952         else
953             eColorInterp = GCI_Undefined;
954     }
955 
956     else {
957         eColorInterp = GCI_Undefined;
958     }
959 
960     return eColorInterp;
961 }
962 
963 /************************************************************************/
964 /*                             GetMinimum()                             */
965 /************************************************************************/
966 
GetMinimum(int * pbSuccess)967 double PostGISRasterRasterBand::GetMinimum( int *pbSuccess )
968 {
969     PostGISRasterDataset * poRDS = (PostGISRasterDataset *)poDS;
970     if( poRDS->bBuildQuadTreeDynamically && poRDS->nTiles == 0 )
971     {
972         if( pbSuccess )
973             *pbSuccess = FALSE;
974         return 0.0;
975     }
976     return VRTSourcedRasterBand::GetMaximum(pbSuccess);
977 }
978 
979 /************************************************************************/
980 /*                             GetMaximum()                             */
981 /************************************************************************/
982 
GetMaximum(int * pbSuccess)983 double PostGISRasterRasterBand::GetMaximum( int *pbSuccess )
984 {
985     PostGISRasterDataset * poRDS = (PostGISRasterDataset *)poDS;
986     if( poRDS->bBuildQuadTreeDynamically && poRDS->nTiles == 0 )
987     {
988         if( pbSuccess )
989             *pbSuccess = FALSE;
990         return 0.0;
991     }
992     return VRTSourcedRasterBand::GetMaximum(pbSuccess);
993 }
994 
995 /************************************************************************/
996 /*                       ComputeRasterMinMax()                          */
997 /************************************************************************/
998 
ComputeRasterMinMax(int bApproxOK,double * adfMinMax)999 CPLErr PostGISRasterRasterBand::ComputeRasterMinMax( int bApproxOK, double* adfMinMax )
1000 {
1001     if( nRasterXSize < 1024 && nRasterYSize < 1024 )
1002         return VRTSourcedRasterBand::ComputeRasterMinMax(bApproxOK, adfMinMax);
1003 
1004     int nOverviewCount = GetOverviewCount();
1005     for(int i = 0; i < nOverviewCount; i++)
1006     {
1007         if( GetOverview(i)->GetXSize() < 1024 && GetOverview(i)->GetYSize() < 1024 )
1008             return GetOverview(i)->ComputeRasterMinMax(bApproxOK, adfMinMax);
1009     }
1010 
1011     return CE_Failure;
1012 }
1013