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

Generated by: LCOV version 1.14