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

Generated by: LCOV version 1.14