776 строки
27 KiB
C++
776 строки
27 KiB
C++
/***********************************************************************
|
|
* File : postgisrasterrasterband.cpp
|
|
* Project: PostGIS Raster driver
|
|
* Purpose: GDAL RasterBand implementation for PostGIS Raster driver
|
|
* Author: Jorge Arevalo, jorge.arevalo@deimos-space.com
|
|
* jorgearevalo@libregis.org
|
|
*
|
|
* Author: David Zwarg, dzwarg@azavea.com
|
|
*
|
|
* Last changes: $Id$
|
|
*
|
|
***********************************************************************
|
|
* Copyright (c) 2009 - 2013, Jorge Arevalo, David Zwarg
|
|
* Copyright (c) 2013-2018, Even Rouault <even.rouault at spatialys.com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
**********************************************************************/
|
|
#include "postgisraster.h"
|
|
|
|
/**
|
|
* \brief Constructor.
|
|
*
|
|
* nBand it is just necessary for overview band creation
|
|
*/
|
|
PostGISRasterRasterBand::PostGISRasterRasterBand(PostGISRasterDataset *poDSIn,
|
|
int nBandIn,
|
|
GDALDataType eDataTypeIn,
|
|
GBool bNoDataValueSetIn,
|
|
double dfNodata)
|
|
: VRTSourcedRasterBand(poDSIn, nBandIn), pszSchema(poDSIn->pszSchema),
|
|
pszTable(poDSIn->pszTable), pszColumn(poDSIn->pszColumn)
|
|
{
|
|
/* Basic properties */
|
|
poDS = poDSIn;
|
|
nBand = nBandIn;
|
|
|
|
eDataType = eDataTypeIn;
|
|
m_bNoDataValueSet = bNoDataValueSetIn;
|
|
m_dfNoDataValue = dfNodata;
|
|
|
|
nRasterXSize = poDS->GetRasterXSize();
|
|
nRasterYSize = poDS->GetRasterYSize();
|
|
|
|
/*******************************************************************
|
|
* Finally, set the block size. We apply the same logic than in VRT
|
|
* driver.
|
|
*
|
|
* We limit the size of a block with MAX_BLOCK_SIZE here to prevent
|
|
* arrangements of just one big tile.
|
|
*
|
|
* This value is just used in case we only have 1 tile in the
|
|
* table. Otherwise, the reading operations are performed by the
|
|
* sources, not the PostGISRasterBand object itself.
|
|
******************************************************************/
|
|
nBlockXSize = atoi(CPLGetConfigOption(
|
|
"PR_BLOCKXSIZE",
|
|
CPLSPrintf("%d", MIN(MAX_BLOCK_SIZE, this->nRasterXSize))));
|
|
nBlockYSize = atoi(CPLGetConfigOption(
|
|
"PR_BLOCKYSIZE",
|
|
CPLSPrintf("%d", MIN(MAX_BLOCK_SIZE, this->nRasterYSize))));
|
|
|
|
#ifdef DEBUG_VERBOSE
|
|
CPLDebug("PostGIS_Raster",
|
|
"PostGISRasterRasterBand constructor: Band size: (%d X %d)",
|
|
nRasterXSize, nRasterYSize);
|
|
|
|
CPLDebug("PostGIS_Raster",
|
|
"PostGISRasterRasterBand::Constructor: "
|
|
"Block size (%dx%d)",
|
|
this->nBlockXSize, this->nBlockYSize);
|
|
#endif
|
|
}
|
|
|
|
/***********************************************
|
|
* \brief: Band destructor
|
|
***********************************************/
|
|
PostGISRasterRasterBand::~PostGISRasterRasterBand()
|
|
{
|
|
}
|
|
|
|
/********************************************************
|
|
* \brief Set nodata value to a buffer
|
|
********************************************************/
|
|
void PostGISRasterRasterBand::NullBuffer(void *pData, int nBufXSize,
|
|
int nBufYSize, GDALDataType eBufType,
|
|
int nPixelSpace, int nLineSpace)
|
|
{
|
|
int j;
|
|
for (j = 0; j < nBufYSize; j++)
|
|
{
|
|
double dfVal = 0.0;
|
|
if (m_bNoDataValueSet)
|
|
dfVal = m_dfNoDataValue;
|
|
GDALCopyWords(&dfVal, GDT_Float64, 0,
|
|
static_cast<GByte *>(pData) + j * nLineSpace, eBufType,
|
|
nPixelSpace, nBufXSize);
|
|
}
|
|
}
|
|
|
|
/********************************************************
|
|
* \brief SortTilesByPKID
|
|
********************************************************/
|
|
static int SortTilesByPKID(const void *a, const void *b)
|
|
{
|
|
const PostGISRasterTileDataset *pa =
|
|
*static_cast<const PostGISRasterTileDataset *const *>(a);
|
|
const PostGISRasterTileDataset *pb =
|
|
*static_cast<const PostGISRasterTileDataset *const *>(b);
|
|
return strcmp(pa->GetPKID(), pb->GetPKID());
|
|
}
|
|
|
|
/**
|
|
* Read/write a region of image data for this band.
|
|
*
|
|
* This method allows reading a region of a PostGISRasterBand into a buffer.
|
|
* The write support is still under development
|
|
*
|
|
* The function fetches all the raster data that intersects with the region
|
|
* provided, and store the data in the GDAL cache.
|
|
*
|
|
* It automatically takes care of data type translation if the data type
|
|
* (eBufType) of the buffer is different than that of the
|
|
* PostGISRasterRasterBand.
|
|
*
|
|
* The nPixelSpace and nLineSpace parameters allow reading into FROM various
|
|
* organization of buffers.
|
|
*
|
|
* @param eRWFlag Either GF_Read to read a region of data (GF_Write, to write
|
|
* a region of data, yet not supported)
|
|
*
|
|
* @param nXOff The pixel offset to the top left corner of the region of the
|
|
* band to be accessed. This would be zero to start FROM the left side.
|
|
*
|
|
* @param nYOff The line offset to the top left corner of the region of the band
|
|
* to be accessed. This would be zero to start FROM the top.
|
|
*
|
|
* @param nXSize The width of the region of the band to be accessed in pixels.
|
|
*
|
|
* @param nYSize The height of the region of the band to be accessed in lines.
|
|
*
|
|
* @param pData The buffer into which the data should be read, or FROM which it
|
|
* should be written. This buffer must contain at least
|
|
* nBufXSize * nBufYSize * nBandCount words of type eBufType. It is organized in
|
|
* left to right,top to bottom pixel order. Spacing is controlled by the
|
|
* nPixelSpace, and nLineSpace parameters.
|
|
*
|
|
* @param nBufXSize the width of the buffer image into which the desired region
|
|
* is to be read, or FROM which it is to be written.
|
|
*
|
|
* @param nBufYSize the height of the buffer image into which the desired region
|
|
* is to be read, or FROM which it is to be written.
|
|
*
|
|
* @param eBufType the type of the pixel values in the pData data buffer. The
|
|
* pixel values will automatically be translated to/FROM the
|
|
* PostGISRasterRasterBand data type as needed.
|
|
*
|
|
* @param nPixelSpace The byte offset FROM the start of one pixel value in pData
|
|
* to the start of the next pixel value within a scanline. If defaulted (0) the
|
|
* size of the datatype eBufType is used.
|
|
*
|
|
* @param nLineSpace The byte offset FROM the start of one scanline in pData to
|
|
* the start of the next. If defaulted (0) the size of the datatype
|
|
* eBufType * nBufXSize is used.
|
|
*
|
|
* @return CE_Failure if the access fails, otherwise CE_None.
|
|
*/
|
|
|
|
CPLErr PostGISRasterRasterBand::IRasterIO(
|
|
GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
|
|
void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
|
|
GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
|
|
{
|
|
/**
|
|
* TODO: Write support not implemented yet
|
|
**/
|
|
if (eRWFlag == GF_Write)
|
|
{
|
|
ReportError(CE_Failure, CPLE_NotSupported,
|
|
"Writing through PostGIS Raster band not supported yet");
|
|
|
|
return CE_Failure;
|
|
}
|
|
|
|
/*******************************************************************
|
|
* Do we have overviews that would be appropriate to satisfy this
|
|
* request?
|
|
******************************************************************/
|
|
if ((nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() > 0)
|
|
{
|
|
if (OverviewRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
|
|
nBufXSize, nBufYSize, eBufType, nPixelSpace,
|
|
nLineSpace, psExtraArg) == CE_None)
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
PostGISRasterDataset *poRDS = cpl::down_cast<PostGISRasterDataset *>(poDS);
|
|
|
|
int bSameWindowAsOtherBand =
|
|
(nXOff == poRDS->nXOffPrev && nYOff == poRDS->nYOffPrev &&
|
|
nXSize == poRDS->nXSizePrev && nYSize == poRDS->nYSizePrev);
|
|
poRDS->nXOffPrev = nXOff;
|
|
poRDS->nYOffPrev = nYOff;
|
|
poRDS->nXSizePrev = nXSize;
|
|
poRDS->nYSizePrev = nYSize;
|
|
|
|
/* Logic to determine if bands are read in order 1, 2, ... N */
|
|
/* If so, then use multi-band caching, otherwise do just single band caching
|
|
*/
|
|
if (poRDS->bAssumeMultiBandReadPattern)
|
|
{
|
|
if (nBand != poRDS->nNextExpectedBand)
|
|
{
|
|
CPLDebug("PostGIS_Raster", "Disabling multi-band caching since "
|
|
"band access pattern does not match");
|
|
poRDS->bAssumeMultiBandReadPattern = false;
|
|
poRDS->nNextExpectedBand = 1;
|
|
}
|
|
else
|
|
{
|
|
poRDS->nNextExpectedBand++;
|
|
if (poRDS->nNextExpectedBand > poRDS->GetRasterCount())
|
|
poRDS->nNextExpectedBand = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (nBand == poRDS->nNextExpectedBand)
|
|
{
|
|
poRDS->nNextExpectedBand++;
|
|
if (poRDS->nNextExpectedBand > poRDS->GetRasterCount())
|
|
{
|
|
CPLDebug("PostGIS_Raster", "Re-enabling multi-band caching");
|
|
poRDS->bAssumeMultiBandReadPattern = true;
|
|
poRDS->nNextExpectedBand = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_VERBOSE
|
|
CPLDebug("PostGIS_Raster",
|
|
"PostGISRasterRasterBand::IRasterIO: "
|
|
"nBand = %d, nXOff = %d, nYOff = %d, nXSize = %d, nYSize = %d, "
|
|
"nBufXSize = %d, nBufYSize = %d",
|
|
nBand, nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize);
|
|
#endif
|
|
|
|
/*******************************************************************
|
|
* Several tiles: we first look in all our sources caches. Missing
|
|
* blocks are queried
|
|
******************************************************************/
|
|
double adfProjWin[8];
|
|
int nFeatureCount = 0;
|
|
CPLRectObj sAoi;
|
|
|
|
poRDS->PolygonFromCoords(nXOff, nYOff, nXOff + nXSize, nYOff + nYSize,
|
|
adfProjWin);
|
|
// (p[6], p[7]) is the minimum (x, y), and (p[2], p[3]) the max
|
|
sAoi.minx = adfProjWin[6];
|
|
sAoi.maxx = adfProjWin[2];
|
|
if (adfProjWin[7] < adfProjWin[3])
|
|
{
|
|
sAoi.miny = adfProjWin[7];
|
|
sAoi.maxy = adfProjWin[3];
|
|
}
|
|
else
|
|
{
|
|
sAoi.maxy = adfProjWin[7];
|
|
sAoi.miny = adfProjWin[3];
|
|
}
|
|
|
|
#ifdef DEBUG_VERBOSE
|
|
CPLDebug("PostGIS_Raster",
|
|
"PostGISRasterRasterBand::IRasterIO: "
|
|
"Intersection box: (%f, %f) - (%f, %f)",
|
|
sAoi.minx, sAoi.miny, sAoi.maxx, sAoi.maxy);
|
|
#endif
|
|
|
|
if (poRDS->hQuadTree == nullptr)
|
|
{
|
|
ReportError(CE_Failure, CPLE_AppDefined,
|
|
"Could not read metadata index.");
|
|
return CE_Failure;
|
|
}
|
|
|
|
NullBuffer(pData, nBufXSize, nBufYSize, eBufType,
|
|
static_cast<int>(nPixelSpace), static_cast<int>(nLineSpace));
|
|
|
|
if (poRDS->bBuildQuadTreeDynamically && !bSameWindowAsOtherBand)
|
|
{
|
|
if (!(poRDS->LoadSources(nXOff, nYOff, nXSize, nYSize, nBand)))
|
|
return CE_Failure;
|
|
}
|
|
|
|
// Matching sources, to avoid a dumb for loop over the sources
|
|
PostGISRasterTileDataset **papsMatchingTiles =
|
|
reinterpret_cast<PostGISRasterTileDataset **>(
|
|
CPLQuadTreeSearch(poRDS->hQuadTree, &sAoi, &nFeatureCount));
|
|
|
|
// No blocks found. This is not an error (the raster may have holes)
|
|
if (nFeatureCount == 0)
|
|
{
|
|
CPLFree(papsMatchingTiles);
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
int i;
|
|
|
|
/**
|
|
* We need to store the max, min coords for the missing tiles in
|
|
* any place. This is as good as any other
|
|
**/
|
|
sAoi.minx = 0.0;
|
|
sAoi.miny = 0.0;
|
|
sAoi.maxx = 0.0;
|
|
sAoi.maxy = 0.0;
|
|
|
|
GIntBig nMemoryRequiredForTiles = 0;
|
|
CPLString osIDsToFetch;
|
|
int nTilesToFetch = 0;
|
|
int nBandDataTypeSize = GDALGetDataTypeSize(eDataType) / 8;
|
|
|
|
// Loop just over the intersecting sources
|
|
for (i = 0; i < nFeatureCount; i++)
|
|
{
|
|
PostGISRasterTileDataset *poTile = papsMatchingTiles[i];
|
|
PostGISRasterTileRasterBand *poTileBand =
|
|
cpl::down_cast<PostGISRasterTileRasterBand *>(
|
|
poTile->GetRasterBand(nBand));
|
|
|
|
nMemoryRequiredForTiles +=
|
|
poTileBand->GetXSize() * poTileBand->GetYSize() * nBandDataTypeSize;
|
|
|
|
// Missing tile: we'll need to query for it
|
|
if (!poTileBand->IsCached())
|
|
{
|
|
|
|
// If we have a PKID, add the tile PKID to the list
|
|
if (poTile->pszPKID != nullptr)
|
|
{
|
|
if (!osIDsToFetch.empty())
|
|
osIDsToFetch += ",";
|
|
osIDsToFetch += "'";
|
|
osIDsToFetch += poTile->pszPKID;
|
|
osIDsToFetch += "'";
|
|
}
|
|
|
|
double dfTileMinX, dfTileMinY, dfTileMaxX, dfTileMaxY;
|
|
poTile->GetExtent(&dfTileMinX, &dfTileMinY, &dfTileMaxX,
|
|
&dfTileMaxY);
|
|
|
|
/**
|
|
* We keep the general max and min values of all the missing
|
|
* tiles, to raise a query that intersect just that area.
|
|
*
|
|
* TODO: In case of just a few tiles and very separated,
|
|
* this strategy is clearly suboptimal. We'll get our
|
|
* missing tiles, but with a lot of other not needed tiles.
|
|
*
|
|
* A possible optimization will be to simply rely on the
|
|
* I/O method of the source (must be implemented), in case
|
|
* we have minus than a reasonable amount of tiles missing.
|
|
* Another criteria to decide would be how separated the
|
|
* tiles are. Two queries for just two adjacent tiles is
|
|
* also a dumb strategy.
|
|
**/
|
|
if (nTilesToFetch == 0)
|
|
{
|
|
sAoi.minx = dfTileMinX;
|
|
sAoi.miny = dfTileMinY;
|
|
sAoi.maxx = dfTileMaxX;
|
|
sAoi.maxy = dfTileMaxY;
|
|
}
|
|
else
|
|
{
|
|
if (dfTileMinX < sAoi.minx)
|
|
sAoi.minx = dfTileMinX;
|
|
|
|
if (dfTileMinY < sAoi.miny)
|
|
sAoi.miny = dfTileMinY;
|
|
|
|
if (dfTileMaxX > sAoi.maxx)
|
|
sAoi.maxx = dfTileMaxX;
|
|
|
|
if (dfTileMaxY > sAoi.maxy)
|
|
sAoi.maxy = dfTileMaxY;
|
|
}
|
|
|
|
nTilesToFetch++;
|
|
}
|
|
}
|
|
|
|
/* Determine caching strategy */
|
|
bool bAllBandCaching = false;
|
|
if (nTilesToFetch > 0)
|
|
{
|
|
GIntBig nCacheMax = GDALGetCacheMax64();
|
|
if (nMemoryRequiredForTiles > nCacheMax)
|
|
{
|
|
CPLDebug("PostGIS_Raster",
|
|
"For best performance, the block cache should be able to "
|
|
"store " CPL_FRMT_GIB
|
|
" bytes for the tiles of the requested window, "
|
|
"but it is only " CPL_FRMT_GIB " byte large",
|
|
nMemoryRequiredForTiles, nCacheMax);
|
|
nTilesToFetch = 0;
|
|
}
|
|
|
|
if (poRDS->GetRasterCount() > 1 && poRDS->bAssumeMultiBandReadPattern)
|
|
{
|
|
GIntBig nMemoryRequiredForTilesAllBands =
|
|
nMemoryRequiredForTiles * poRDS->GetRasterCount();
|
|
if (nMemoryRequiredForTilesAllBands <= nCacheMax)
|
|
{
|
|
bAllBandCaching = true;
|
|
}
|
|
else
|
|
{
|
|
CPLDebug("PostGIS_Raster",
|
|
"Caching only this band, but not all bands. "
|
|
"Cache should be " CPL_FRMT_GIB " byte large for that",
|
|
nMemoryRequiredForTilesAllBands);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Raise a query for missing tiles and cache them
|
|
if (nTilesToFetch > 0)
|
|
{
|
|
|
|
/**
|
|
* There are several options here, to raise the query.
|
|
* - Get all the tiles which PKID is in a list of missing
|
|
* PKIDs.
|
|
* - Get all the tiles that intersect a polygon constructed
|
|
* based on the (min - max) values calculated before.
|
|
* - Get all the tiles with upper left pixel included in the
|
|
* range (min - max) calculated before.
|
|
*
|
|
* The first option is the most efficient one when a PKID exists.
|
|
* After that, the second one is the most efficient one when a
|
|
* spatial index exists.
|
|
* The third one is the only one available when neither a PKID or
|
|
*spatial index exist.
|
|
**/
|
|
|
|
CPLString osSchemaI(CPLQuotedSQLIdentifier(pszSchema));
|
|
CPLString osTableI(CPLQuotedSQLIdentifier(pszTable));
|
|
CPLString osColumnI(CPLQuotedSQLIdentifier(pszColumn));
|
|
|
|
CPLString osWHERE;
|
|
if (!osIDsToFetch.empty() &&
|
|
(poRDS->bIsFastPK || !(poRDS->HasSpatialIndex())))
|
|
{
|
|
if (nTilesToFetch < poRDS->m_nTiles ||
|
|
poRDS->bBuildQuadTreeDynamically)
|
|
{
|
|
osWHERE += poRDS->pszPrimaryKeyName;
|
|
osWHERE += " IN (";
|
|
osWHERE += osIDsToFetch;
|
|
osWHERE += ")";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (poRDS->HasSpatialIndex())
|
|
{
|
|
osWHERE += CPLSPrintf(
|
|
"%s && "
|
|
"ST_GeomFromText('POLYGON((%.18f %.18f,%.18f %.18f,%.18f "
|
|
"%.18f,%.18f %.18f,%.18f %.18f))')",
|
|
osColumnI.c_str(), adfProjWin[0], adfProjWin[1],
|
|
adfProjWin[2], adfProjWin[3], adfProjWin[4], adfProjWin[5],
|
|
adfProjWin[6], adfProjWin[7], adfProjWin[0], adfProjWin[1]);
|
|
}
|
|
else
|
|
{
|
|
#define EPS 1e-5
|
|
osWHERE += CPLSPrintf(
|
|
"ST_UpperLeftX(%s)"
|
|
" BETWEEN %f AND %f AND ST_UpperLeftY(%s) BETWEEN "
|
|
"%f AND %f",
|
|
osColumnI.c_str(), sAoi.minx - EPS, sAoi.maxx + EPS,
|
|
osColumnI.c_str(), sAoi.miny - EPS, sAoi.maxy + EPS);
|
|
}
|
|
}
|
|
|
|
if (poRDS->pszWhere != nullptr)
|
|
{
|
|
if (!osWHERE.empty())
|
|
osWHERE += " AND ";
|
|
osWHERE += "(";
|
|
osWHERE += poRDS->pszWhere;
|
|
osWHERE += ")";
|
|
}
|
|
|
|
bool bCanUseClientSide = true;
|
|
if (poRDS->eOutDBResolution == OutDBResolution::CLIENT_SIDE_IF_POSSIBLE)
|
|
{
|
|
bCanUseClientSide =
|
|
poRDS->CanUseClientSideOutDB(bAllBandCaching, nBand, osWHERE);
|
|
}
|
|
|
|
CPLString osRasterToFetch;
|
|
if (bAllBandCaching)
|
|
osRasterToFetch = osColumnI;
|
|
else
|
|
osRasterToFetch.Printf("ST_Band(%s, %d)", osColumnI.c_str(), nBand);
|
|
if (poRDS->eOutDBResolution == OutDBResolution::SERVER_SIDE ||
|
|
!bCanUseClientSide)
|
|
{
|
|
osRasterToFetch =
|
|
"encode(ST_AsBinary(" + osRasterToFetch + ",TRUE),'hex')";
|
|
}
|
|
|
|
CPLString osCommand;
|
|
osCommand.Printf("SELECT %s, ST_Metadata(%s), %s FROM %s.%s",
|
|
(poRDS->GetPrimaryKeyRef()) ? poRDS->GetPrimaryKeyRef()
|
|
: "NULL",
|
|
osColumnI.c_str(), osRasterToFetch.c_str(),
|
|
osSchemaI.c_str(), osTableI.c_str());
|
|
if (!osWHERE.empty())
|
|
{
|
|
osCommand += " WHERE " + osWHERE;
|
|
}
|
|
|
|
PGresult *poResult = PQexec(poRDS->poConn, osCommand.c_str());
|
|
|
|
#ifdef DEBUG_QUERY
|
|
CPLDebug("PostGIS_Raster",
|
|
"PostGISRasterRasterBand::IRasterIO(): Query = \"%s\" --> "
|
|
"number of rows = %d",
|
|
osCommand.c_str(), poResult ? PQntuples(poResult) : 0);
|
|
#endif
|
|
|
|
if (poResult == nullptr ||
|
|
PQresultStatus(poResult) != PGRES_TUPLES_OK ||
|
|
PQntuples(poResult) < 0)
|
|
{
|
|
|
|
if (poResult)
|
|
PQclear(poResult);
|
|
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"PostGISRasterRasterBand::IRasterIO(): %s",
|
|
PQerrorMessage(poRDS->poConn));
|
|
|
|
// Free the object that holds pointers to matching tiles
|
|
CPLFree(papsMatchingTiles);
|
|
return CE_Failure;
|
|
}
|
|
|
|
/**
|
|
* No data. Return the buffer filled with nodata values
|
|
**/
|
|
else if (PQntuples(poResult) == 0)
|
|
{
|
|
PQclear(poResult);
|
|
|
|
// Free the object that holds pointers to matching tiles
|
|
CPLFree(papsMatchingTiles);
|
|
return CE_None;
|
|
}
|
|
|
|
/**
|
|
* Ok, we loop over the results
|
|
**/
|
|
int nTuples = PQntuples(poResult);
|
|
for (i = 0; i < nTuples; i++)
|
|
{
|
|
const char *pszPKID = PQgetvalue(poResult, i, 0);
|
|
const char *pszMetadata = PQgetvalue(poResult, i, 1);
|
|
const char *pszRaster = PQgetvalue(poResult, i, 2);
|
|
poRDS->CacheTile(pszMetadata, pszRaster, pszPKID, nBand,
|
|
bAllBandCaching);
|
|
} // All tiles have been added to cache
|
|
|
|
PQclear(poResult);
|
|
} // End missing tiles
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Overlay each source in turn over top this. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
CPLErr eErr = CE_None;
|
|
/* Sort tiles by ascending PKID, so that the draw order is deterministic. */
|
|
if (poRDS->GetPrimaryKeyRef() != nullptr)
|
|
{
|
|
qsort(papsMatchingTiles, nFeatureCount,
|
|
sizeof(PostGISRasterTileDataset *), SortTilesByPKID);
|
|
}
|
|
|
|
for (i = 0; i < nFeatureCount && eErr == CE_None; i++)
|
|
{
|
|
PostGISRasterTileDataset *poTile = papsMatchingTiles[i];
|
|
PostGISRasterTileRasterBand *poTileBand =
|
|
cpl::down_cast<PostGISRasterTileRasterBand *>(
|
|
poTile->GetRasterBand(nBand));
|
|
eErr = poTileBand->poSource->RasterIO(
|
|
eDataType, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
|
|
nBufYSize, eBufType, nPixelSpace, nLineSpace, nullptr);
|
|
}
|
|
|
|
// Free the object that holds pointers to matching tiles
|
|
CPLFree(papsMatchingTiles);
|
|
|
|
return eErr;
|
|
}
|
|
|
|
/**
|
|
* \brief Set the no data value for this band.
|
|
* Parameters:
|
|
* - double: The nodata value
|
|
* Returns:
|
|
* - CE_None.
|
|
*/
|
|
CPLErr PostGISRasterRasterBand::SetNoDataValue(double dfNewValue)
|
|
{
|
|
m_dfNoDataValue = dfNewValue;
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/**
|
|
* \brief Fetch the no data value for this band.
|
|
* Parameters:
|
|
* - int *: pointer to a boolean to use to indicate if a value is actually
|
|
* associated with this layer. May be NULL (default).
|
|
* Returns:
|
|
* - double: the nodata value for this band.
|
|
*/
|
|
double PostGISRasterRasterBand::GetNoDataValue(int *pbSuccess)
|
|
{
|
|
if (pbSuccess != nullptr)
|
|
*pbSuccess = m_bNoDataValueSet;
|
|
|
|
return m_dfNoDataValue;
|
|
}
|
|
|
|
/***************************************************
|
|
* \brief Return the number of overview layers available
|
|
***************************************************/
|
|
int PostGISRasterRasterBand::GetOverviewCount()
|
|
{
|
|
PostGISRasterDataset *poRDS = cpl::down_cast<PostGISRasterDataset *>(poDS);
|
|
return poRDS->GetOverviewCount();
|
|
}
|
|
|
|
/**********************************************************
|
|
* \brief Fetch overview raster band object
|
|
**********************************************************/
|
|
GDALRasterBand *PostGISRasterRasterBand::GetOverview(int i)
|
|
{
|
|
if (i < 0 || i >= GetOverviewCount())
|
|
return nullptr;
|
|
|
|
PostGISRasterDataset *poRDS = cpl::down_cast<PostGISRasterDataset *>(poDS);
|
|
PostGISRasterDataset *poOverviewDS = poRDS->GetOverviewDS(i);
|
|
if (poOverviewDS->nBands == 0)
|
|
{
|
|
if (!poOverviewDS->SetRasterProperties(nullptr) ||
|
|
poOverviewDS->GetRasterCount() != poRDS->GetRasterCount())
|
|
{
|
|
CPLDebug("PostGIS_Raster",
|
|
"Request for overview %d of band %d failed", i, nBand);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
return poOverviewDS->GetRasterBand(nBand);
|
|
}
|
|
|
|
/**
|
|
* \brief How should this band be interpreted as color?
|
|
* GCI_Undefined is returned when the format doesn't know anything about the
|
|
* color interpretation.
|
|
**/
|
|
GDALColorInterp PostGISRasterRasterBand::GetColorInterpretation()
|
|
{
|
|
if (poDS->GetRasterCount() == 1)
|
|
{
|
|
m_eColorInterp = GCI_GrayIndex;
|
|
}
|
|
|
|
else if (poDS->GetRasterCount() == 3)
|
|
{
|
|
if (nBand == 1)
|
|
m_eColorInterp = GCI_RedBand;
|
|
else if (nBand == 2)
|
|
m_eColorInterp = GCI_GreenBand;
|
|
else if (nBand == 3)
|
|
m_eColorInterp = GCI_BlueBand;
|
|
else
|
|
m_eColorInterp = GCI_Undefined;
|
|
}
|
|
|
|
else
|
|
{
|
|
m_eColorInterp = GCI_Undefined;
|
|
}
|
|
|
|
return m_eColorInterp;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetMinimum() */
|
|
/************************************************************************/
|
|
|
|
double PostGISRasterRasterBand::GetMinimum(int *pbSuccess)
|
|
{
|
|
PostGISRasterDataset *poRDS = cpl::down_cast<PostGISRasterDataset *>(poDS);
|
|
if (poRDS->bBuildQuadTreeDynamically && poRDS->m_nTiles == 0)
|
|
{
|
|
if (pbSuccess)
|
|
*pbSuccess = FALSE;
|
|
return 0.0;
|
|
}
|
|
return VRTSourcedRasterBand::GetMaximum(pbSuccess);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetMaximum() */
|
|
/************************************************************************/
|
|
|
|
double PostGISRasterRasterBand::GetMaximum(int *pbSuccess)
|
|
{
|
|
PostGISRasterDataset *poRDS = cpl::down_cast<PostGISRasterDataset *>(poDS);
|
|
if (poRDS->bBuildQuadTreeDynamically && poRDS->m_nTiles == 0)
|
|
{
|
|
if (pbSuccess)
|
|
*pbSuccess = FALSE;
|
|
return 0.0;
|
|
}
|
|
return VRTSourcedRasterBand::GetMaximum(pbSuccess);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ComputeRasterMinMax() */
|
|
/************************************************************************/
|
|
|
|
CPLErr PostGISRasterRasterBand::ComputeRasterMinMax(int bApproxOK,
|
|
double *adfMinMax)
|
|
{
|
|
if (nRasterXSize < 1024 && nRasterYSize < 1024)
|
|
return VRTSourcedRasterBand::ComputeRasterMinMax(bApproxOK, adfMinMax);
|
|
|
|
int nOverviewCount = GetOverviewCount();
|
|
for (int i = 0; i < nOverviewCount; i++)
|
|
{
|
|
auto poOverview = GetOverview(i);
|
|
if (poOverview->GetXSize() < 1024 && poOverview->GetYSize() < 1024)
|
|
return poOverview->ComputeRasterMinMax(bApproxOK, adfMinMax);
|
|
}
|
|
|
|
return CE_Failure;
|
|
}
|