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

Generated by: LCOV version 1.14