LCOV - code coverage report
Current view: top level - frmts/postgisraster - postgisrasterrasterband.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 0 256 0.0 %
Date: 2024-05-04 12:52:34 Functions: 0 14 0.0 %

          Line data    Source code
       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-2018, Even Rouault <even.rouault at spatialys.com>
      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 is just necessary for overview band creation
      42             :  */
      43           0 : PostGISRasterRasterBand::PostGISRasterRasterBand(PostGISRasterDataset *poDSIn,
      44             :                                                  int nBandIn,
      45             :                                                  GDALDataType eDataTypeIn,
      46             :                                                  GBool bNoDataValueSetIn,
      47           0 :                                                  double dfNodata)
      48           0 :     : VRTSourcedRasterBand(poDSIn, nBandIn), pszSchema(poDSIn->pszSchema),
      49           0 :       pszTable(poDSIn->pszTable), pszColumn(poDSIn->pszColumn)
      50             : {
      51             :     /* Basic properties */
      52           0 :     poDS = poDSIn;
      53           0 :     nBand = nBandIn;
      54             : 
      55           0 :     eDataType = eDataTypeIn;
      56           0 :     m_bNoDataValueSet = bNoDataValueSetIn;
      57           0 :     m_dfNoDataValue = dfNodata;
      58             : 
      59           0 :     nRasterXSize = poDS->GetRasterXSize();
      60           0 :     nRasterYSize = poDS->GetRasterYSize();
      61             : 
      62             :     /*******************************************************************
      63             :      * Finally, set the block size. We apply the same logic than in VRT
      64             :      * driver.
      65             :      *
      66             :      * We limit the size of a block with MAX_BLOCK_SIZE here to prevent
      67             :      * arrangements of just one big tile.
      68             :      *
      69             :      * This value is just used in case we only have 1 tile in the
      70             :      * table. Otherwise, the reading operations are performed by the
      71             :      * sources, not the PostGISRasterBand object itself.
      72             :      ******************************************************************/
      73           0 :     nBlockXSize = atoi(CPLGetConfigOption(
      74             :         "PR_BLOCKXSIZE",
      75           0 :         CPLSPrintf("%d", MIN(MAX_BLOCK_SIZE, this->nRasterXSize))));
      76           0 :     nBlockYSize = atoi(CPLGetConfigOption(
      77             :         "PR_BLOCKYSIZE",
      78           0 :         CPLSPrintf("%d", MIN(MAX_BLOCK_SIZE, this->nRasterYSize))));
      79             : 
      80             : #ifdef DEBUG_VERBOSE
      81             :     CPLDebug("PostGIS_Raster",
      82             :              "PostGISRasterRasterBand constructor: Band size: (%d X %d)",
      83             :              nRasterXSize, nRasterYSize);
      84             : 
      85             :     CPLDebug("PostGIS_Raster",
      86             :              "PostGISRasterRasterBand::Constructor: "
      87             :              "Block size (%dx%d)",
      88             :              this->nBlockXSize, this->nBlockYSize);
      89             : #endif
      90           0 : }
      91             : 
      92             : /***********************************************
      93             :  * \brief: Band destructor
      94             :  ***********************************************/
      95           0 : PostGISRasterRasterBand::~PostGISRasterRasterBand()
      96             : {
      97           0 : }
      98             : 
      99             : /********************************************************
     100             :  * \brief Set nodata value to a buffer
     101             :  ********************************************************/
     102           0 : void PostGISRasterRasterBand::NullBuffer(void *pData, int nBufXSize,
     103             :                                          int nBufYSize, GDALDataType eBufType,
     104             :                                          int nPixelSpace, int nLineSpace)
     105             : {
     106             :     int j;
     107           0 :     for (j = 0; j < nBufYSize; j++)
     108             :     {
     109           0 :         double dfVal = 0.0;
     110           0 :         if (m_bNoDataValueSet)
     111           0 :             dfVal = m_dfNoDataValue;
     112           0 :         GDALCopyWords(&dfVal, GDT_Float64, 0,
     113           0 :                       static_cast<GByte *>(pData) + j * nLineSpace, eBufType,
     114             :                       nPixelSpace, nBufXSize);
     115             :     }
     116           0 : }
     117             : 
     118             : /********************************************************
     119             :  * \brief SortTilesByPKID
     120             :  ********************************************************/
     121           0 : static int SortTilesByPKID(const void *a, const void *b)
     122             : {
     123           0 :     const PostGISRasterTileDataset *pa =
     124             :         *static_cast<const PostGISRasterTileDataset *const *>(a);
     125           0 :     const PostGISRasterTileDataset *pb =
     126             :         *static_cast<const PostGISRasterTileDataset *const *>(b);
     127           0 :     return strcmp(pa->GetPKID(), pb->GetPKID());
     128             : }
     129             : 
     130             : /**
     131             :  * Read/write a region of image data for this band.
     132             :  *
     133             :  * This method allows reading a region of a PostGISRasterBand into a buffer.
     134             :  * The write support is still under development
     135             :  *
     136             :  * The function fetches all the raster data that intersects with the region
     137             :  * provided, and store the data in the GDAL cache.
     138             :  *
     139             :  * It automatically takes care of data type translation if the data type
     140             :  * (eBufType) of the buffer is different than that of the
     141             :  * PostGISRasterRasterBand.
     142             :  *
     143             :  * The nPixelSpace and nLineSpace parameters allow reading into FROM various
     144             :  * organization of buffers.
     145             :  *
     146             :  * @param eRWFlag Either GF_Read to read a region of data (GF_Write, to write
     147             :  * a region of data, yet not supported)
     148             :  *
     149             :  * @param nXOff The pixel offset to the top left corner of the region of the
     150             :  * band to be accessed. This would be zero to start FROM the left side.
     151             :  *
     152             :  * @param nYOff The line offset to the top left corner of the region of the band
     153             :  * to be accessed. This would be zero to start FROM the top.
     154             :  *
     155             :  * @param nXSize The width of the region of the band to be accessed in pixels.
     156             :  *
     157             :  * @param nYSize The height of the region of the band to be accessed in lines.
     158             :  *
     159             :  * @param pData The buffer into which the data should be read, or FROM which it
     160             :  * should be written. This buffer must contain at least
     161             :  * nBufXSize * nBufYSize * nBandCount words of type eBufType. It is organized in
     162             :  * left to right,top to bottom pixel order. Spacing is controlled by the
     163             :  * nPixelSpace, and nLineSpace parameters.
     164             :  *
     165             :  * @param nBufXSize the width of the buffer image into which the desired region
     166             :  * is to be read, or FROM which it is to be written.
     167             :  *
     168             :  * @param nBufYSize the height of the buffer image into which the desired region
     169             :  * is to be read, or FROM which it is to be written.
     170             :  *
     171             :  * @param eBufType the type of the pixel values in the pData data buffer. The
     172             :  * pixel values will automatically be translated to/FROM the
     173             :  * PostGISRasterRasterBand data type as needed.
     174             :  *
     175             :  * @param nPixelSpace The byte offset FROM the start of one pixel value in pData
     176             :  * to the start of the next pixel value within a scanline. If defaulted (0) the
     177             :  * size of the datatype eBufType is used.
     178             :  *
     179             :  * @param nLineSpace The byte offset FROM the start of one scanline in pData to
     180             :  * the start of the next. If defaulted (0) the size of the datatype
     181             :  * eBufType * nBufXSize is used.
     182             :  *
     183             :  * @return CE_Failure if the access fails, otherwise CE_None.
     184             :  */
     185             : 
     186           0 : CPLErr PostGISRasterRasterBand::IRasterIO(
     187             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
     188             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
     189             :     GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
     190             : {
     191             :     /**
     192             :      * TODO: Write support not implemented yet
     193             :      **/
     194           0 :     if (eRWFlag == GF_Write)
     195             :     {
     196           0 :         ReportError(CE_Failure, CPLE_NotSupported,
     197             :                     "Writing through PostGIS Raster band not supported yet");
     198             : 
     199           0 :         return CE_Failure;
     200             :     }
     201             : 
     202             :     /*******************************************************************
     203             :      * Do we have overviews that would be appropriate to satisfy this
     204             :      * request?
     205             :      ******************************************************************/
     206           0 :     if ((nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() > 0)
     207             :     {
     208           0 :         if (OverviewRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
     209             :                              nBufXSize, nBufYSize, eBufType, nPixelSpace,
     210           0 :                              nLineSpace, psExtraArg) == CE_None)
     211             : 
     212           0 :             return CE_None;
     213             :     }
     214             : 
     215           0 :     PostGISRasterDataset *poRDS = cpl::down_cast<PostGISRasterDataset *>(poDS);
     216             : 
     217           0 :     int bSameWindowAsOtherBand =
     218           0 :         (nXOff == poRDS->nXOffPrev && nYOff == poRDS->nYOffPrev &&
     219           0 :          nXSize == poRDS->nXSizePrev && nYSize == poRDS->nYSizePrev);
     220           0 :     poRDS->nXOffPrev = nXOff;
     221           0 :     poRDS->nYOffPrev = nYOff;
     222           0 :     poRDS->nXSizePrev = nXSize;
     223           0 :     poRDS->nYSizePrev = nYSize;
     224             : 
     225             :     /* Logic to determine if bands are read in order 1, 2, ... N */
     226             :     /* If so, then use multi-band caching, otherwise do just single band caching
     227             :      */
     228           0 :     if (poRDS->bAssumeMultiBandReadPattern)
     229             :     {
     230           0 :         if (nBand != poRDS->nNextExpectedBand)
     231             :         {
     232           0 :             CPLDebug("PostGIS_Raster", "Disabling multi-band caching since "
     233             :                                        "band access pattern does not match");
     234           0 :             poRDS->bAssumeMultiBandReadPattern = false;
     235           0 :             poRDS->nNextExpectedBand = 1;
     236             :         }
     237             :         else
     238             :         {
     239           0 :             poRDS->nNextExpectedBand++;
     240           0 :             if (poRDS->nNextExpectedBand > poRDS->GetRasterCount())
     241           0 :                 poRDS->nNextExpectedBand = 1;
     242             :         }
     243             :     }
     244             :     else
     245             :     {
     246           0 :         if (nBand == poRDS->nNextExpectedBand)
     247             :         {
     248           0 :             poRDS->nNextExpectedBand++;
     249           0 :             if (poRDS->nNextExpectedBand > poRDS->GetRasterCount())
     250             :             {
     251           0 :                 CPLDebug("PostGIS_Raster", "Re-enabling multi-band caching");
     252           0 :                 poRDS->bAssumeMultiBandReadPattern = true;
     253           0 :                 poRDS->nNextExpectedBand = 1;
     254             :             }
     255             :         }
     256             :     }
     257             : 
     258             : #ifdef DEBUG_VERBOSE
     259             :     CPLDebug("PostGIS_Raster",
     260             :              "PostGISRasterRasterBand::IRasterIO: "
     261             :              "nBand = %d, nXOff = %d, nYOff = %d, nXSize = %d, nYSize = %d, "
     262             :              "nBufXSize = %d, nBufYSize = %d",
     263             :              nBand, nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize);
     264             : #endif
     265             : 
     266             :     /*******************************************************************
     267             :      * Several tiles: we first look in all our sources caches. Missing
     268             :      * blocks are queried
     269             :      ******************************************************************/
     270             :     double adfProjWin[8];
     271           0 :     int nFeatureCount = 0;
     272             :     CPLRectObj sAoi;
     273             : 
     274           0 :     poRDS->PolygonFromCoords(nXOff, nYOff, nXOff + nXSize, nYOff + nYSize,
     275             :                              adfProjWin);
     276             :     // (p[6], p[7]) is the minimum (x, y), and (p[2], p[3]) the max
     277           0 :     sAoi.minx = adfProjWin[6];
     278           0 :     sAoi.maxx = adfProjWin[2];
     279           0 :     if (adfProjWin[7] < adfProjWin[3])
     280             :     {
     281           0 :         sAoi.miny = adfProjWin[7];
     282           0 :         sAoi.maxy = adfProjWin[3];
     283             :     }
     284             :     else
     285             :     {
     286           0 :         sAoi.maxy = adfProjWin[7];
     287           0 :         sAoi.miny = adfProjWin[3];
     288             :     }
     289             : 
     290             : #ifdef DEBUG_VERBOSE
     291             :     CPLDebug("PostGIS_Raster",
     292             :              "PostGISRasterRasterBand::IRasterIO: "
     293             :              "Intersection box: (%f, %f) - (%f, %f)",
     294             :              sAoi.minx, sAoi.miny, sAoi.maxx, sAoi.maxy);
     295             : #endif
     296             : 
     297           0 :     if (poRDS->hQuadTree == nullptr)
     298             :     {
     299           0 :         ReportError(CE_Failure, CPLE_AppDefined,
     300             :                     "Could not read metadata index.");
     301           0 :         return CE_Failure;
     302             :     }
     303             : 
     304           0 :     NullBuffer(pData, nBufXSize, nBufYSize, eBufType,
     305             :                static_cast<int>(nPixelSpace), static_cast<int>(nLineSpace));
     306             : 
     307           0 :     if (poRDS->bBuildQuadTreeDynamically && !bSameWindowAsOtherBand)
     308             :     {
     309           0 :         if (!(poRDS->LoadSources(nXOff, nYOff, nXSize, nYSize, nBand)))
     310           0 :             return CE_Failure;
     311             :     }
     312             : 
     313             :     // Matching sources, to avoid a dumb for loop over the sources
     314             :     PostGISRasterTileDataset **papsMatchingTiles =
     315             :         reinterpret_cast<PostGISRasterTileDataset **>(
     316           0 :             CPLQuadTreeSearch(poRDS->hQuadTree, &sAoi, &nFeatureCount));
     317             : 
     318             :     // No blocks found. This is not an error (the raster may have holes)
     319           0 :     if (nFeatureCount == 0)
     320             :     {
     321           0 :         CPLFree(papsMatchingTiles);
     322             : 
     323           0 :         return CE_None;
     324             :     }
     325             : 
     326             :     int i;
     327             : 
     328             :     /**
     329             :      * We need to store the max, min coords for the missing tiles in
     330             :      * any place. This is as good as any other
     331             :      **/
     332           0 :     sAoi.minx = 0.0;
     333           0 :     sAoi.miny = 0.0;
     334           0 :     sAoi.maxx = 0.0;
     335           0 :     sAoi.maxy = 0.0;
     336             : 
     337           0 :     GIntBig nMemoryRequiredForTiles = 0;
     338           0 :     CPLString osIDsToFetch;
     339           0 :     int nTilesToFetch = 0;
     340           0 :     int nBandDataTypeSize = GDALGetDataTypeSize(eDataType) / 8;
     341             : 
     342             :     // Loop just over the intersecting sources
     343           0 :     for (i = 0; i < nFeatureCount; i++)
     344             :     {
     345           0 :         PostGISRasterTileDataset *poTile = papsMatchingTiles[i];
     346             :         PostGISRasterTileRasterBand *poTileBand =
     347           0 :             cpl::down_cast<PostGISRasterTileRasterBand *>(
     348             :                 poTile->GetRasterBand(nBand));
     349             : 
     350           0 :         nMemoryRequiredForTiles +=
     351           0 :             static_cast<GIntBig>(poTileBand->GetXSize()) *
     352           0 :             poTileBand->GetYSize() * nBandDataTypeSize;
     353             : 
     354             :         // Missing tile: we'll need to query for it
     355           0 :         if (!poTileBand->IsCached())
     356             :         {
     357             : 
     358             :             // If we have a PKID, add the tile PKID to the list
     359           0 :             if (poTile->pszPKID != nullptr)
     360             :             {
     361           0 :                 if (!osIDsToFetch.empty())
     362           0 :                     osIDsToFetch += ",";
     363           0 :                 osIDsToFetch += "'";
     364           0 :                 osIDsToFetch += poTile->pszPKID;
     365           0 :                 osIDsToFetch += "'";
     366             :             }
     367             : 
     368             :             double dfTileMinX, dfTileMinY, dfTileMaxX, dfTileMaxY;
     369           0 :             poTile->GetExtent(&dfTileMinX, &dfTileMinY, &dfTileMaxX,
     370             :                               &dfTileMaxY);
     371             : 
     372             :             /**
     373             :              * We keep the general max and min values of all the missing
     374             :              * tiles, to raise a query that intersect just that area.
     375             :              *
     376             :              * TODO: In case of just a few tiles and very separated,
     377             :              * this strategy is clearly suboptimal. We'll get our
     378             :              * missing tiles, but with a lot of other not needed tiles.
     379             :              *
     380             :              * A possible optimization will be to simply rely on the
     381             :              * I/O method of the source (must be implemented), in case
     382             :              * we have minus than a reasonable amount of tiles missing.
     383             :              * Another criteria to decide would be how separated the
     384             :              * tiles are. Two queries for just two adjacent tiles is
     385             :              * also a dumb strategy.
     386             :              **/
     387           0 :             if (nTilesToFetch == 0)
     388             :             {
     389           0 :                 sAoi.minx = dfTileMinX;
     390           0 :                 sAoi.miny = dfTileMinY;
     391           0 :                 sAoi.maxx = dfTileMaxX;
     392           0 :                 sAoi.maxy = dfTileMaxY;
     393             :             }
     394             :             else
     395             :             {
     396           0 :                 if (dfTileMinX < sAoi.minx)
     397           0 :                     sAoi.minx = dfTileMinX;
     398             : 
     399           0 :                 if (dfTileMinY < sAoi.miny)
     400           0 :                     sAoi.miny = dfTileMinY;
     401             : 
     402           0 :                 if (dfTileMaxX > sAoi.maxx)
     403           0 :                     sAoi.maxx = dfTileMaxX;
     404             : 
     405           0 :                 if (dfTileMaxY > sAoi.maxy)
     406           0 :                     sAoi.maxy = dfTileMaxY;
     407             :             }
     408             : 
     409           0 :             nTilesToFetch++;
     410             :         }
     411             :     }
     412             : 
     413             :     /* Determine caching strategy */
     414           0 :     bool bAllBandCaching = false;
     415           0 :     if (nTilesToFetch > 0)
     416             :     {
     417           0 :         GIntBig nCacheMax = GDALGetCacheMax64();
     418           0 :         if (nMemoryRequiredForTiles > nCacheMax)
     419             :         {
     420           0 :             CPLDebug("PostGIS_Raster",
     421             :                      "For best performance, the block cache should be able to "
     422             :                      "store " CPL_FRMT_GIB
     423             :                      " bytes for the tiles of the requested window, "
     424             :                      "but it is only " CPL_FRMT_GIB " byte large",
     425             :                      nMemoryRequiredForTiles, nCacheMax);
     426           0 :             nTilesToFetch = 0;
     427             :         }
     428             : 
     429           0 :         if (poRDS->GetRasterCount() > 1 && poRDS->bAssumeMultiBandReadPattern)
     430             :         {
     431             :             GIntBig nMemoryRequiredForTilesAllBands =
     432           0 :                 nMemoryRequiredForTiles * poRDS->GetRasterCount();
     433           0 :             if (nMemoryRequiredForTilesAllBands <= nCacheMax)
     434             :             {
     435           0 :                 bAllBandCaching = true;
     436             :             }
     437             :             else
     438             :             {
     439           0 :                 CPLDebug("PostGIS_Raster",
     440             :                          "Caching only this band, but not all bands. "
     441             :                          "Cache should be " CPL_FRMT_GIB " byte large for that",
     442             :                          nMemoryRequiredForTilesAllBands);
     443             :             }
     444             :         }
     445             :     }
     446             : 
     447             :     // Raise a query for missing tiles and cache them
     448           0 :     if (nTilesToFetch > 0)
     449             :     {
     450             : 
     451             :         /**
     452             :          * There are several options here, to raise the query.
     453             :          * - Get all the tiles which PKID is in a list of missing
     454             :          *   PKIDs.
     455             :          * - Get all the tiles that intersect a polygon constructed
     456             :          *   based on the (min - max) values calculated before.
     457             :          * - Get all the tiles with upper left pixel included in the
     458             :          *   range (min - max) calculated before.
     459             :          *
     460             :          * The first option is the most efficient one when a PKID exists.
     461             :          * After that, the second one is the most efficient one when a
     462             :          * spatial index exists.
     463             :          * The third one is the only one available when neither a PKID or
     464             :          *spatial index exist.
     465             :          **/
     466             : 
     467           0 :         CPLString osSchemaI(CPLQuotedSQLIdentifier(pszSchema));
     468           0 :         CPLString osTableI(CPLQuotedSQLIdentifier(pszTable));
     469           0 :         CPLString osColumnI(CPLQuotedSQLIdentifier(pszColumn));
     470             : 
     471           0 :         CPLString osWHERE;
     472           0 :         if (!osIDsToFetch.empty() &&
     473           0 :             (poRDS->bIsFastPK || !(poRDS->HasSpatialIndex())))
     474             :         {
     475           0 :             if (nTilesToFetch < poRDS->m_nTiles ||
     476           0 :                 poRDS->bBuildQuadTreeDynamically)
     477             :             {
     478           0 :                 osWHERE += poRDS->pszPrimaryKeyName;
     479           0 :                 osWHERE += " IN (";
     480           0 :                 osWHERE += osIDsToFetch;
     481           0 :                 osWHERE += ")";
     482             :             }
     483             :         }
     484             :         else
     485             :         {
     486           0 :             if (poRDS->HasSpatialIndex())
     487             :             {
     488             :                 osWHERE += CPLSPrintf(
     489             :                     "%s && "
     490             :                     "ST_GeomFromText('POLYGON((%.18f %.18f,%.18f %.18f,%.18f "
     491             :                     "%.18f,%.18f %.18f,%.18f %.18f))')",
     492             :                     osColumnI.c_str(), adfProjWin[0], adfProjWin[1],
     493             :                     adfProjWin[2], adfProjWin[3], adfProjWin[4], adfProjWin[5],
     494           0 :                     adfProjWin[6], adfProjWin[7], adfProjWin[0], adfProjWin[1]);
     495             :             }
     496             :             else
     497             :             {
     498             : #define EPS 1e-5
     499             :                 osWHERE += CPLSPrintf(
     500             :                     "ST_UpperLeftX(%s)"
     501             :                     " BETWEEN %f AND %f AND ST_UpperLeftY(%s) BETWEEN "
     502             :                     "%f AND %f",
     503           0 :                     osColumnI.c_str(), sAoi.minx - EPS, sAoi.maxx + EPS,
     504           0 :                     osColumnI.c_str(), sAoi.miny - EPS, sAoi.maxy + EPS);
     505             :             }
     506             :         }
     507             : 
     508           0 :         if (poRDS->pszWhere != nullptr)
     509             :         {
     510           0 :             if (!osWHERE.empty())
     511           0 :                 osWHERE += " AND ";
     512           0 :             osWHERE += "(";
     513           0 :             osWHERE += poRDS->pszWhere;
     514           0 :             osWHERE += ")";
     515             :         }
     516             : 
     517           0 :         bool bCanUseClientSide = true;
     518           0 :         if (poRDS->eOutDBResolution == OutDBResolution::CLIENT_SIDE_IF_POSSIBLE)
     519             :         {
     520             :             bCanUseClientSide =
     521           0 :                 poRDS->CanUseClientSideOutDB(bAllBandCaching, nBand, osWHERE);
     522             :         }
     523             : 
     524           0 :         CPLString osRasterToFetch;
     525           0 :         if (bAllBandCaching)
     526           0 :             osRasterToFetch = osColumnI;
     527             :         else
     528           0 :             osRasterToFetch.Printf("ST_Band(%s, %d)", osColumnI.c_str(), nBand);
     529           0 :         if (poRDS->eOutDBResolution == OutDBResolution::SERVER_SIDE ||
     530           0 :             !bCanUseClientSide)
     531             :         {
     532             :             osRasterToFetch =
     533           0 :                 "encode(ST_AsBinary(" + osRasterToFetch + ",TRUE),'hex')";
     534             :         }
     535             : 
     536           0 :         CPLString osCommand;
     537             :         osCommand.Printf("SELECT %s, ST_Metadata(%s), %s FROM %s.%s",
     538           0 :                          (poRDS->GetPrimaryKeyRef()) ? poRDS->GetPrimaryKeyRef()
     539             :                                                      : "NULL",
     540             :                          osColumnI.c_str(), osRasterToFetch.c_str(),
     541           0 :                          osSchemaI.c_str(), osTableI.c_str());
     542           0 :         if (!osWHERE.empty())
     543             :         {
     544           0 :             osCommand += " WHERE " + osWHERE;
     545             :         }
     546             : 
     547           0 :         PGresult *poResult = PQexec(poRDS->poConn, osCommand.c_str());
     548             : 
     549             : #ifdef DEBUG_QUERY
     550             :         CPLDebug("PostGIS_Raster",
     551             :                  "PostGISRasterRasterBand::IRasterIO(): Query = \"%s\" --> "
     552             :                  "number of rows = %d",
     553             :                  osCommand.c_str(), poResult ? PQntuples(poResult) : 0);
     554             : #endif
     555             : 
     556           0 :         if (poResult == nullptr ||
     557           0 :             PQresultStatus(poResult) != PGRES_TUPLES_OK ||
     558           0 :             PQntuples(poResult) < 0)
     559             :         {
     560             : 
     561           0 :             if (poResult)
     562           0 :                 PQclear(poResult);
     563             : 
     564           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     565             :                      "PostGISRasterRasterBand::IRasterIO(): %s",
     566           0 :                      PQerrorMessage(poRDS->poConn));
     567             : 
     568             :             // Free the object that holds pointers to matching tiles
     569           0 :             CPLFree(papsMatchingTiles);
     570           0 :             return CE_Failure;
     571             :         }
     572             : 
     573             :         /**
     574             :          * No data. Return the buffer filled with nodata values
     575             :          **/
     576           0 :         else if (PQntuples(poResult) == 0)
     577             :         {
     578           0 :             PQclear(poResult);
     579             : 
     580             :             // Free the object that holds pointers to matching tiles
     581           0 :             CPLFree(papsMatchingTiles);
     582           0 :             return CE_None;
     583             :         }
     584             : 
     585             :         /**
     586             :          * Ok, we loop over the results
     587             :          **/
     588           0 :         int nTuples = PQntuples(poResult);
     589           0 :         for (i = 0; i < nTuples; i++)
     590             :         {
     591           0 :             const char *pszPKID = PQgetvalue(poResult, i, 0);
     592           0 :             const char *pszMetadata = PQgetvalue(poResult, i, 1);
     593           0 :             const char *pszRaster = PQgetvalue(poResult, i, 2);
     594           0 :             poRDS->CacheTile(pszMetadata, pszRaster, pszPKID, nBand,
     595             :                              bAllBandCaching);
     596             :         }  // All tiles have been added to cache
     597             : 
     598           0 :         PQclear(poResult);
     599             :     }  // End missing tiles
     600             : 
     601             :     /* -------------------------------------------------------------------- */
     602             :     /*      Overlay each source in turn over top this.                      */
     603             :     /* -------------------------------------------------------------------- */
     604             : 
     605           0 :     CPLErr eErr = CE_None;
     606             :     /* Sort tiles by ascending PKID, so that the draw order is deterministic. */
     607           0 :     if (poRDS->GetPrimaryKeyRef() != nullptr)
     608             :     {
     609           0 :         qsort(papsMatchingTiles, nFeatureCount,
     610             :               sizeof(PostGISRasterTileDataset *), SortTilesByPKID);
     611             :     }
     612             : 
     613           0 :     VRTSource::WorkingState oWorkingState;
     614           0 :     for (i = 0; i < nFeatureCount && eErr == CE_None; i++)
     615             :     {
     616           0 :         PostGISRasterTileDataset *poTile = papsMatchingTiles[i];
     617             :         PostGISRasterTileRasterBand *poTileBand =
     618           0 :             cpl::down_cast<PostGISRasterTileRasterBand *>(
     619             :                 poTile->GetRasterBand(nBand));
     620           0 :         eErr = poTileBand->poSource->RasterIO(
     621             :             eDataType, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
     622             :             nBufYSize, eBufType, nPixelSpace, nLineSpace, nullptr,
     623           0 :             oWorkingState);
     624             :     }
     625             : 
     626             :     // Free the object that holds pointers to matching tiles
     627           0 :     CPLFree(papsMatchingTiles);
     628             : 
     629           0 :     return eErr;
     630             : }
     631             : 
     632             : /**
     633             :  * \brief Set the no data value for this band.
     634             :  * Parameters:
     635             :  *  - double: The nodata value
     636             :  * Returns:
     637             :  *  - CE_None.
     638             :  */
     639           0 : CPLErr PostGISRasterRasterBand::SetNoDataValue(double dfNewValue)
     640             : {
     641           0 :     m_dfNoDataValue = dfNewValue;
     642             : 
     643           0 :     return CE_None;
     644             : }
     645             : 
     646             : /**
     647             :  * \brief Fetch the no data value for this band.
     648             :  * Parameters:
     649             :  *  - int *: pointer to a boolean to use to indicate if a value is actually
     650             :  *          associated with this layer. May be NULL (default).
     651             :  *  Returns:
     652             :  *  - double: the nodata value for this band.
     653             :  */
     654           0 : double PostGISRasterRasterBand::GetNoDataValue(int *pbSuccess)
     655             : {
     656           0 :     if (pbSuccess != nullptr)
     657           0 :         *pbSuccess = m_bNoDataValueSet;
     658             : 
     659           0 :     return m_dfNoDataValue;
     660             : }
     661             : 
     662             : /***************************************************
     663             :  * \brief Return the number of overview layers available
     664             :  ***************************************************/
     665           0 : int PostGISRasterRasterBand::GetOverviewCount()
     666             : {
     667           0 :     PostGISRasterDataset *poRDS = cpl::down_cast<PostGISRasterDataset *>(poDS);
     668           0 :     return poRDS->GetOverviewCount();
     669             : }
     670             : 
     671             : /**********************************************************
     672             :  * \brief Fetch overview raster band object
     673             :  **********************************************************/
     674           0 : GDALRasterBand *PostGISRasterRasterBand::GetOverview(int i)
     675             : {
     676           0 :     if (i < 0 || i >= GetOverviewCount())
     677           0 :         return nullptr;
     678             : 
     679           0 :     PostGISRasterDataset *poRDS = cpl::down_cast<PostGISRasterDataset *>(poDS);
     680           0 :     PostGISRasterDataset *poOverviewDS = poRDS->GetOverviewDS(i);
     681           0 :     if (!poOverviewDS)
     682             :     {
     683           0 :         CPLAssert(false);
     684             :         return nullptr;
     685             :     }
     686           0 :     if (poOverviewDS->nBands == 0)
     687             :     {
     688           0 :         if (!poOverviewDS->SetRasterProperties(nullptr) ||
     689           0 :             poOverviewDS->GetRasterCount() != poRDS->GetRasterCount())
     690             :         {
     691           0 :             CPLDebug("PostGIS_Raster",
     692             :                      "Request for overview %d of band %d failed", i, nBand);
     693           0 :             return nullptr;
     694             :         }
     695             :     }
     696             : 
     697           0 :     return poOverviewDS->GetRasterBand(nBand);
     698             : }
     699             : 
     700             : /**
     701             :  * \brief How should this band be interpreted as color?
     702             :  * GCI_Undefined is returned when the format doesn't know anything about the
     703             :  * color interpretation.
     704             :  **/
     705           0 : GDALColorInterp PostGISRasterRasterBand::GetColorInterpretation()
     706             : {
     707           0 :     if (poDS->GetRasterCount() == 1)
     708             :     {
     709           0 :         m_eColorInterp = GCI_GrayIndex;
     710             :     }
     711             : 
     712           0 :     else if (poDS->GetRasterCount() == 3)
     713             :     {
     714           0 :         if (nBand == 1)
     715           0 :             m_eColorInterp = GCI_RedBand;
     716           0 :         else if (nBand == 2)
     717           0 :             m_eColorInterp = GCI_GreenBand;
     718           0 :         else if (nBand == 3)
     719           0 :             m_eColorInterp = GCI_BlueBand;
     720             :         else
     721           0 :             m_eColorInterp = GCI_Undefined;
     722             :     }
     723             : 
     724             :     else
     725             :     {
     726           0 :         m_eColorInterp = GCI_Undefined;
     727             :     }
     728             : 
     729           0 :     return m_eColorInterp;
     730             : }
     731             : 
     732             : /************************************************************************/
     733             : /*                             GetMinimum()                             */
     734             : /************************************************************************/
     735             : 
     736           0 : double PostGISRasterRasterBand::GetMinimum(int *pbSuccess)
     737             : {
     738           0 :     PostGISRasterDataset *poRDS = cpl::down_cast<PostGISRasterDataset *>(poDS);
     739           0 :     if (poRDS->bBuildQuadTreeDynamically && poRDS->m_nTiles == 0)
     740             :     {
     741           0 :         if (pbSuccess)
     742           0 :             *pbSuccess = FALSE;
     743           0 :         return 0.0;
     744             :     }
     745           0 :     return VRTSourcedRasterBand::GetMaximum(pbSuccess);
     746             : }
     747             : 
     748             : /************************************************************************/
     749             : /*                             GetMaximum()                             */
     750             : /************************************************************************/
     751             : 
     752           0 : double PostGISRasterRasterBand::GetMaximum(int *pbSuccess)
     753             : {
     754           0 :     PostGISRasterDataset *poRDS = cpl::down_cast<PostGISRasterDataset *>(poDS);
     755           0 :     if (poRDS->bBuildQuadTreeDynamically && poRDS->m_nTiles == 0)
     756             :     {
     757           0 :         if (pbSuccess)
     758           0 :             *pbSuccess = FALSE;
     759           0 :         return 0.0;
     760             :     }
     761           0 :     return VRTSourcedRasterBand::GetMaximum(pbSuccess);
     762             : }
     763             : 
     764             : /************************************************************************/
     765             : /*                       ComputeRasterMinMax()                          */
     766             : /************************************************************************/
     767             : 
     768           0 : CPLErr PostGISRasterRasterBand::ComputeRasterMinMax(int bApproxOK,
     769             :                                                     double *adfMinMax)
     770             : {
     771           0 :     if (nRasterXSize < 1024 && nRasterYSize < 1024)
     772           0 :         return VRTSourcedRasterBand::ComputeRasterMinMax(bApproxOK, adfMinMax);
     773             : 
     774           0 :     int nOverviewCount = GetOverviewCount();
     775           0 :     for (int i = 0; i < nOverviewCount; i++)
     776             :     {
     777           0 :         auto poOverview = GetOverview(i);
     778           0 :         if (poOverview->GetXSize() < 1024 && poOverview->GetYSize() < 1024)
     779           0 :             return poOverview->ComputeRasterMinMax(bApproxOK, adfMinMax);
     780             :     }
     781             : 
     782           0 :     return CE_Failure;
     783             : }

Generated by: LCOV version 1.14