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

Generated by: LCOV version 1.14