/***************************************************** * \brief Read a natural block of raster band data *****************************************************/ CPLErr PostGISRasterTileRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, CPL_UNUSED int nBlockYOff, void * pImage) { CPLString osCommand; PGresult * poResult = NULL; int nWKBLength = 0; int nPixelSize = GDALGetDataTypeSize(eDataType)/8; PostGISRasterTileDataset * poRTDS = (PostGISRasterTileDataset *)poDS; // Get by PKID if (poRTDS->poRDS->pszPrimaryKeyName) { osCommand.Printf("select st_band(%s, %d) from %s.%s where " "%s = '%s'", poRTDS->poRDS->pszColumn, nBand, poRTDS->poRDS->pszSchema, poRTDS->poRDS->pszTable, poRTDS->poRDS->pszPrimaryKeyName, poRTDS->pszPKID); } // Get by upperleft else { osCommand.Printf("select st_band(%s, %d) from %s.%s where " "abs(ST_UpperLeftX(%s) - %.8f) < 1e-8 and abs(ST_UpperLeftY(%s) - %.8f) < 1e-8", poRTDS->poRDS->pszColumn, nBand, poRTDS->poRDS->pszSchema, poRTDS->poRDS->pszTable, poRTDS->poRDS->pszColumn, poRTDS->adfGeoTransform[GEOTRSFRM_TOPLEFT_X], poRTDS->poRDS->pszColumn, poRTDS->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y]); } poResult = PQexec(poRTDS->poRDS->poConn, osCommand.c_str()); #ifdef DEBUG_QUERY CPLDebug("PostGIS_Raster", "PostGISRasterTileRasterBand::IReadBlock(): " "Query = \"%s\" --> number of rows = %d", osCommand.c_str(), poResult ? PQntuples(poResult) : 0 ); #endif if (poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK || PQntuples(poResult) <= 0) { if (poResult) PQclear(poResult); ReportError(CE_Failure, CPLE_AppDefined, "Error getting block of data (upperpixel = %f, %f)", poRTDS->adfGeoTransform[GEOTRSFRM_TOPLEFT_X], poRTDS->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y]); return CE_Failure; } // TODO: Check this if (bIsOffline) { CPLError(CE_Failure, CPLE_AppDefined, "This raster has outdb " "storage. This feature isn't still available"); PQclear(poResult); return CE_Failure; } /* Copy only data size, without payload */ int nExpectedDataSize = nBlockXSize * nBlockYSize * nPixelSize; GByte * pbyData = CPLHexToBinary(PQgetvalue(poResult, 0, 0), &nWKBLength); int nExpectedWKBLength = RASTER_HEADER_SIZE + BAND_SIZE(nPixelSize, nExpectedDataSize); CPLErr eRet = CE_None; if( nWKBLength != nExpectedWKBLength ) { CPLDebug("PostGIS_Raster", "nWKBLength=%d, nExpectedWKBLength=%d", nWKBLength, nExpectedWKBLength ); eRet = CE_Failure; } else { GByte * pbyDataToRead = (GByte*)GET_BAND_DATA(pbyData,1, nPixelSize, nExpectedDataSize); // Do byte-swapping if necessary */ int bIsLittleEndian = (pbyData[0] == 1); #ifdef CPL_LSB int bSwap = !bIsLittleEndian; #else int bSwap = bIsLittleEndian; #endif if( bSwap && nPixelSize > 1 ) { GDALSwapWords( pbyDataToRead, nPixelSize, nBlockXSize * nBlockYSize, nPixelSize ); } memcpy(pImage, pbyDataToRead, nExpectedDataSize); } CPLFree(pbyData); PQclear(poResult); return eRet; }
/***************************************************** * \brief Read a natural block of raster band data *****************************************************/ CPLErr PostGISRasterRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void * pImage) { PGresult * poResult = NULL; CPLString osCommand; int nXOff = 0; int nYOff = 0; int nNaturalBlockXSize = 0; int nNaturalBlockYSize = 0; double adfProjWin[8]; PostGISRasterDataset * poRDS = (PostGISRasterDataset *)poDS; int nPixelSize = GDALGetDataTypeSize(eDataType) / 8; // Construct a polygon to intersect with GetBlockSize(&nNaturalBlockXSize, &nNaturalBlockYSize); nXOff = nBlockXOff * nNaturalBlockXSize; nYOff = nBlockYOff * nNaturalBlockYSize; poRDS->PolygonFromCoords(nXOff, nYOff, nXOff + nNaturalBlockXSize, nYOff + nNaturalBlockYSize, adfProjWin); // Raise the query if (poRDS->pszWhere == NULL) { osCommand.Printf("SELECT st_band(%s, %d) FROM %s.%s " "WHERE st_intersects(%s, ST_PolygonFromText" "('POLYGON((%.17f %.17f, %.17f %.17f, %.17f %.17f, %.17f " "%.17f, %.17f %.17f))', %d))", pszColumn, nBand, pszSchema, pszTable, pszColumn, adfProjWin[0], adfProjWin[1], adfProjWin[2], adfProjWin[3], adfProjWin[4], adfProjWin[5], adfProjWin[6], adfProjWin[7], adfProjWin[0], adfProjWin[1], poRDS->nSrid); } else { osCommand.Printf("SELECT st_band(%s, %d) FROM %s.%s WHERE (%s) " "AND st_intersects(%s, ST_PolygonFromText" "('POLYGON((%.17f %.17f, %.17f %.17f, %.17f %.17f, %.17f " "%.17f, %.17f %.17f))', %d))", pszColumn, nBand, pszSchema, pszTable, poRDS->pszWhere, pszColumn, adfProjWin[0], adfProjWin[1], adfProjWin[2], adfProjWin[3], adfProjWin[4], adfProjWin[5], adfProjWin[6], adfProjWin[7], adfProjWin[0], adfProjWin[1], poRDS->nSrid); } #ifdef DEBUG_QUERY CPLDebug("PostGIS_Raster", "PostGISRasterRasterBand::IReadBlock(): Query = %s", osCommand.c_str()); #endif poResult = PQexec(poRDS->poConn, osCommand.c_str()); if (poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK || PQntuples(poResult) < 0) { if (poResult) PQclear(poResult); ReportError(CE_Failure, CPLE_AppDefined, "Error retrieving raster data FROM database"); CPLDebug("PostGIS_Raster", "PostGISRasterRasterBand::IRasterIO(): %s", PQerrorMessage(poRDS->poConn)); return CE_Failure; } /** * No data. Return the buffer filled with nodata values **/ else if (PQntuples(poResult) == 0) { PQclear(poResult); CPLDebug("PostGIS_Raster", "PostGISRasterRasterBand::IRasterIO(): Null block"); NullBlock(pImage); return CE_None; } /** * Ok, we get the data. Only data size, without payload * * TODO: Check byte order **/ int nExpectedDataSize = nNaturalBlockXSize * nNaturalBlockYSize * nPixelSize; int nWKBLength = 0; GByte * pbyData = CPLHexToBinary(PQgetvalue(poResult, 0, 0), &nWKBLength); char * pbyDataToRead = (char*)GET_BAND_DATA(pbyData,nBand, nPixelSize, nExpectedDataSize); memcpy(pImage, pbyDataToRead, nExpectedDataSize * sizeof(char)); CPLDebug("PostGIS_Raster", "IReadBlock: Copied %d bytes FROM block " "(%d, %d) to %p", nExpectedDataSize, nBlockXOff, nBlockYOff, pImage); CPLFree(pbyData); PQclear(poResult); return CE_None; }
/***************************************************** * \brief Read a natural block of raster band data *****************************************************/ CPLErr PostGISRasterTileRasterBand::IReadBlock(int /*nBlockXOff*/, int /*nBlockYOff*/, void * pImage) { CPLString osCommand; PGresult * poResult = nullptr; int nWKBLength = 0; const int nPixelSize = GDALGetDataTypeSizeBytes(eDataType); PostGISRasterTileDataset * poRTDS = cpl::down_cast<PostGISRasterTileDataset *>(poDS); const double dfTileUpperLeftX = poRTDS->adfGeoTransform[GEOTRSFRM_TOPLEFT_X]; const double dfTileUpperLeftY = poRTDS->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y]; const double dfTileResX = poRTDS->adfGeoTransform[1]; const double dfTileResY = poRTDS->adfGeoTransform[5]; const int nTileXSize = nBlockXSize; const int nTileYSize = nBlockYSize; CPLString osRasterToFetch; osRasterToFetch.Printf("ST_Band(%s, %d)", poRTDS->poRDS->pszColumn, nBand); // We don't honour CLIENT_SIDE_IF_POSSIBLE since it would be likely too // costly in that context. if( poRTDS->poRDS->eOutDBResolution != OutDBResolution::CLIENT_SIDE ) { osRasterToFetch = "encode(ST_AsBinary(" + osRasterToFetch + ",TRUE),'hex')"; } osCommand.Printf("SELECT %s FROM %s.%s WHERE ", osRasterToFetch.c_str(), poRTDS->poRDS->pszSchema, poRTDS->poRDS->pszTable); // Get by PKID if (poRTDS->poRDS->pszPrimaryKeyName) { osCommand += CPLSPrintf("%s = '%s'", poRTDS->poRDS->pszPrimaryKeyName, poRTDS->pszPKID); } // Get by upperleft else { osCommand += CPLSPrintf( "abs(ST_UpperLeftX(%s) - %.8f) < 1e-8 and abs(ST_UpperLeftY(%s) - %.8f) < 1e-8", poRTDS->poRDS->pszColumn, dfTileUpperLeftX, poRTDS->poRDS->pszColumn, dfTileUpperLeftY); } poResult = PQexec(poRTDS->poRDS->poConn, osCommand.c_str()); #ifdef DEBUG_QUERY CPLDebug("PostGIS_Raster", "PostGISRasterTileRasterBand::IReadBlock(): " "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) { CPLString osError; if( PQresultStatus(poResult) == PGRES_FATAL_ERROR ) { const char *pszError = PQerrorMessage( poRTDS->poRDS->poConn ); if( pszError ) osError = pszError; } if (poResult) PQclear(poResult); ReportError(CE_Failure, CPLE_AppDefined, "Error getting block of data (upperpixel = %f, %f): %s", dfTileUpperLeftX, dfTileUpperLeftY, osError.c_str()); return CE_Failure; } /* Copy only data size, without payload */ int nExpectedDataSize = nBlockXSize * nBlockYSize * nPixelSize; struct CPLFreer { void operator() (GByte* x) const { CPLFree(x); } }; std::unique_ptr<GByte, CPLFreer> pbyDataAutoFreed( CPLHexToBinary(PQgetvalue(poResult, 0, 0), &nWKBLength)); GByte* pbyData = pbyDataAutoFreed.get(); PQclear(poResult); const int nMinimumWKBLength = RASTER_HEADER_SIZE + BAND_SIZE(1, nPixelSize); if( nWKBLength < nMinimumWKBLength ) { CPLDebug("PostGIS_Raster", "nWKBLength=%d. too short. Expected at least %d", nWKBLength, nMinimumWKBLength ); return CE_Failure; } // Is it indb-raster ? if( (pbyData[RASTER_HEADER_SIZE] & 0x80) == 0 ) { int nExpectedWKBLength = RASTER_HEADER_SIZE + BAND_SIZE(nPixelSize, nExpectedDataSize); if( nWKBLength != nExpectedWKBLength ) { CPLDebug("PostGIS_Raster", "nWKBLength=%d, nExpectedWKBLength=%d", nWKBLength, nExpectedWKBLength ); return CE_Failure; } GByte * pbyDataToRead = GET_BAND_DATA(pbyData,1, nPixelSize,nExpectedDataSize); // Do byte-swapping if necessary */ const bool bIsLittleEndian = (pbyData[0] == 1); #ifdef CPL_LSB const bool bSwap = !bIsLittleEndian; #else const bool bSwap = bIsLittleEndian; #endif if( bSwap && nPixelSize > 1 ) { GDALSwapWords( pbyDataToRead, nPixelSize, nBlockXSize * nBlockYSize, nPixelSize ); } memcpy(pImage, pbyDataToRead, nExpectedDataSize); } else { int nCurOffset = RASTER_HEADER_SIZE; if( !poRTDS->poRDS->LoadOutdbRaster(nCurOffset, eDataType, nBand, pbyData, nWKBLength, pImage, dfTileUpperLeftX, dfTileUpperLeftY, dfTileResX, dfTileResY, nTileXSize, nTileYSize) ) { return CE_Failure; } } return CE_None; }