CPLErr VRTRawRasterBand::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 ) { if( m_poRawRaster == NULL ) { CPLError( CE_Failure, CPLE_AppDefined, "No raw raster band configured on VRTRawRasterBand." ); return CE_Failure; } if( eRWFlag == GF_Write && eAccess == GA_ReadOnly ) { CPLError( CE_Failure, CPLE_NoWriteAccess, "Attempt to write to read only dataset in" "VRTRawRasterBand::IRasterIO()." ); 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; } m_poRawRaster->SetAccess(eAccess); return m_poRawRaster->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace, psExtraArg ); }
CPLErr RawRasterBand::IRasterIO( GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize, void * pData, int nBufXSize, int nBufYSize, GDALDataType eBufType, int nPixelSpace, int nLineSpace ) { int nBandDataSize = GDALGetDataTypeSize(eDataType) / 8; int nBufDataSize = GDALGetDataTypeSize( eBufType ) / 8; int nBytesToRW = nPixelOffset * nXSize; /* -------------------------------------------------------------------- */ /* Use direct IO without caching if: */ /* */ /* GDAL_ONE_BIG_READ is enabled */ /* */ /* or */ /* */ /* the length of a scanline on disk is more than 50000 bytes, and the */ /* width of the requested chunk is less than 40% of the whole scanline */ /* and none of the requested scanlines are already in the cache. */ /* -------------------------------------------------------------------- */ if( nPixelOffset < 0 ) { return GDALRasterBand::IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace ); } if ( !CSLTestBoolean( CPLGetConfigOption( "GDAL_ONE_BIG_READ", "NO") ) ) { if ( nLineSize < 50000 || nBytesToRW > nLineSize / 5 * 2 || IsLineLoaded( nYOff, nYSize ) ) { return GDALRasterBand::IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace ); } } /* ==================================================================== */ /* Read data. */ /* ==================================================================== */ if ( eRWFlag == GF_Read ) { /* -------------------------------------------------------------------- */ /* 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 ) == CE_None ) return CE_None; } /* ==================================================================== */ /* 1. Simplest case when we should get contiguous block */ /* of uninterleaved pixels. */ /* ==================================================================== */ if ( nXSize == GetXSize() && nXSize == nBufXSize && nYSize == nBufYSize && eBufType == eDataType && nPixelOffset == nBandDataSize && nPixelSpace == nBufDataSize && nLineSpace == nPixelSpace * nXSize ) { if ( AccessBlock( nImgOffset + (vsi_l_offset)nYOff * nLineOffset + nXOff, nXSize * nYSize * nBandDataSize, pData ) != CE_None ) { CPLError( CE_Failure, CPLE_FileIO, "Failed to read %d bytes at %lu.", nXSize * nYSize * nBandDataSize, (unsigned long) (nImgOffset + (vsi_l_offset)nYOff * nLineOffset + nXOff) ); } } /* ==================================================================== */ /* 2. Case when we need deinterleave and/or subsample data. */ /* ==================================================================== */ else { GByte *pabyData; double dfSrcXInc, dfSrcYInc; int iLine; dfSrcXInc = (double)nXSize / nBufXSize; dfSrcYInc = (double)nYSize / nBufYSize; pabyData = (GByte *) CPLMalloc( nBytesToRW ); for ( iLine = 0; iLine < nBufYSize; iLine++ ) { if ( AccessBlock( nImgOffset + ((vsi_l_offset)nYOff + (int)(iLine * dfSrcYInc)) * nLineOffset + nXOff * nPixelOffset, nBytesToRW, pabyData ) != CE_None ) { CPLError( CE_Failure, CPLE_FileIO, "Failed to read %d bytes at %lu.", nBytesToRW, (unsigned long)(nImgOffset + ((vsi_l_offset)nYOff + (int)(iLine * dfSrcYInc)) * nLineOffset + nXOff * nPixelOffset) ); } /* -------------------------------------------------------------------- */ /* Copy data from disk buffer to user block buffer and subsample, */ /* if needed. */ /* -------------------------------------------------------------------- */ if ( nXSize == nBufXSize && nYSize == nBufYSize ) { GDALCopyWords( pabyData, eDataType, nPixelOffset, (GByte *)pData + iLine * nLineSpace, eBufType, nPixelSpace, nXSize ); } else { int iPixel; for ( iPixel = 0; iPixel < nBufXSize; iPixel++ ) { GDALCopyWords( pabyData + (int)(iPixel * dfSrcXInc) * nPixelOffset, eDataType, 0, (GByte *)pData + iLine * nLineSpace + iPixel * nBufDataSize, eBufType, nPixelSpace, 1 ); } } } CPLFree( pabyData ); } } /* ==================================================================== */ /* Write data. */ /* ==================================================================== */ else { int nBytesActuallyWritten; /* ==================================================================== */ /* 1. Simplest case when we should write contiguous block */ /* of uninterleaved pixels. */ /* ==================================================================== */ if ( nXSize == GetXSize() && nXSize == nBufXSize && nYSize == nBufYSize && eBufType == eDataType && nPixelOffset == nBandDataSize && nPixelSpace == nBufDataSize && nLineSpace == nPixelSpace * nXSize ) { /* -------------------------------------------------------------------- */ /* Byte swap the data buffer, if required. */ /* -------------------------------------------------------------------- */ if( !bNativeOrder && eDataType != GDT_Byte ) { if( GDALDataTypeIsComplex( eDataType ) ) { int nWordSize; nWordSize = GDALGetDataTypeSize(eDataType)/16; GDALSwapWords( pData, nWordSize, nXSize, nPixelOffset ); GDALSwapWords( ((GByte *) pData) + nWordSize, nWordSize, nXSize, nPixelOffset ); } else GDALSwapWords( pData, nBandDataSize, nXSize, nPixelOffset ); } /* -------------------------------------------------------------------- */ /* Seek to the right block. */ /* -------------------------------------------------------------------- */ if( Seek( nImgOffset + (vsi_l_offset)nYOff * nLineOffset + nXOff, SEEK_SET) == -1 ) { CPLError( CE_Failure, CPLE_FileIO, "Failed to seek to %lu to write data.\n", (unsigned long)(nImgOffset + (vsi_l_offset)nYOff * nLineOffset + nXOff) ); return CE_Failure; } /* -------------------------------------------------------------------- */ /* Write the block. */ /* -------------------------------------------------------------------- */ nBytesToRW = nXSize * nYSize * nBandDataSize; nBytesActuallyWritten = Write( pData, 1, nBytesToRW ); if( nBytesActuallyWritten < nBytesToRW ) { CPLError( CE_Failure, CPLE_FileIO, "Failed to write %d bytes to file. %d bytes written", nBytesToRW, nBytesActuallyWritten ); return CE_Failure; } /* -------------------------------------------------------------------- */ /* Byte swap (if necessary) back into machine order so the */ /* buffer is still usable for reading purposes. */ /* -------------------------------------------------------------------- */ if( !bNativeOrder && eDataType != GDT_Byte ) { if( GDALDataTypeIsComplex( eDataType ) ) { int nWordSize; nWordSize = GDALGetDataTypeSize(eDataType)/16; GDALSwapWords( pData, nWordSize, nXSize, nPixelOffset ); GDALSwapWords( ((GByte *) pData) + nWordSize, nWordSize, nXSize, nPixelOffset ); } else GDALSwapWords( pData, nBandDataSize, nXSize, nPixelOffset ); } } /* ==================================================================== */ /* 2. Case when we need deinterleave and/or subsample data. */ /* ==================================================================== */ else { GByte *pabyData; double dfSrcXInc, dfSrcYInc; vsi_l_offset nBlockOff; int iLine; dfSrcXInc = (double)nXSize / nBufXSize; dfSrcYInc = (double)nYSize / nBufYSize; pabyData = (GByte *) CPLMalloc( nBytesToRW ); for ( iLine = 0; iLine < nBufYSize; iLine++ ) { nBlockOff = nImgOffset + ((vsi_l_offset)nYOff + (int)(iLine*dfSrcYInc))*nLineOffset + nXOff * nPixelOffset; /* -------------------------------------------------------------------- */ /* If the data for this band is completely contiguous we don't */ /* have to worry about pre-reading from disk. */ /* -------------------------------------------------------------------- */ if( nPixelOffset > nBandDataSize ) AccessBlock( nBlockOff, nBytesToRW, pabyData ); /* -------------------------------------------------------------------- */ /* Copy data from user block buffer to disk buffer and subsample, */ /* if needed. */ /* -------------------------------------------------------------------- */ if ( nXSize == nBufXSize && nYSize == nBufYSize ) { GDALCopyWords( (GByte *)pData + iLine * nLineSpace, eBufType, nPixelSpace, pabyData, eDataType, nPixelOffset, nXSize ); } else { int iPixel; for ( iPixel = 0; iPixel < nBufXSize; iPixel++ ) { GDALCopyWords( (GByte *)pData+iLine*nLineSpace + iPixel * nBufDataSize, eBufType, nPixelSpace, pabyData + (int)(iPixel * dfSrcXInc) * nPixelOffset, eDataType, 0, 1 ); } } /* -------------------------------------------------------------------- */ /* Byte swap the data buffer, if required. */ /* -------------------------------------------------------------------- */ if( !bNativeOrder && eDataType != GDT_Byte ) { if( GDALDataTypeIsComplex( eDataType ) ) { int nWordSize; nWordSize = GDALGetDataTypeSize(eDataType)/16; GDALSwapWords( pabyData, nWordSize, nXSize, nPixelOffset ); GDALSwapWords( ((GByte *) pabyData) + nWordSize, nWordSize, nXSize, nPixelOffset ); } else GDALSwapWords( pabyData, nBandDataSize, nXSize, nPixelOffset ); } /* -------------------------------------------------------------------- */ /* Seek to the right line in block. */ /* -------------------------------------------------------------------- */ if( Seek( nBlockOff, SEEK_SET) == -1 ) { CPLError( CE_Failure, CPLE_FileIO, "Failed to seek to %ld to read.\n", (long)nBlockOff ); return CE_Failure; } /* -------------------------------------------------------------------- */ /* Write the line of block. */ /* -------------------------------------------------------------------- */ nBytesActuallyWritten = Write( pabyData, 1, nBytesToRW ); if( nBytesActuallyWritten < nBytesToRW ) { CPLError( CE_Failure, CPLE_FileIO, "Failed to write %d bytes to file. %d bytes written", nBytesToRW, nBytesActuallyWritten ); return CE_Failure; } /* -------------------------------------------------------------------- */ /* Byte swap (if necessary) back into machine order so the */ /* buffer is still usable for reading purposes. */ /* -------------------------------------------------------------------- */ if( !bNativeOrder && eDataType != GDT_Byte ) { if( GDALDataTypeIsComplex( eDataType ) ) { int nWordSize; nWordSize = GDALGetDataTypeSize(eDataType)/16; GDALSwapWords( pabyData, nWordSize, nXSize, nPixelOffset ); GDALSwapWords( ((GByte *) pabyData) + nWordSize, nWordSize, nXSize, nPixelOffset ); } else GDALSwapWords( pabyData, nBandDataSize, nXSize, nPixelOffset ); } } bDirty = TRUE; CPLFree( pabyData ); } } return CE_None; }
/** * Read/write a region of image data for this band. * * Each of the sources for this derived band will be read and passed to * the derived band pixel function. The pixel function is responsible * for applying whatever algorithm is necessary to generate this band's * pixels from the sources. * * The sources will be read using the transfer type specified for sources * using SetSourceTransferType(). If no transfer type has been set for * this derived band, the band's data type will be used as the transfer type. * * @see gdalrasterband * * @param eRWFlag Either GF_Read to read a region of data, or GT_Write to * write a region of data. * * @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 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 GDALRasterBand * 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 the size of the datatype * eBufType * nBufXSize is used. * * @return CE_Failure if the access fails, otherwise CE_None. */ CPLErr VRTDerivedRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize, void * pData, int nBufXSize, int nBufYSize, GDALDataType eBufType, int nPixelSpace, int nLineSpace ) { GDALDerivedPixelFunc pfnPixelFunc; void **pBuffers; CPLErr eErr = CE_None; int iSource, ii, typesize, sourcesize; GDALDataType eSrcType; if( eRWFlag == GF_Write ) { CPLError( CE_Failure, CPLE_AppDefined, "Writing through VRTSourcedRasterBand is not supported." ); return CE_Failure; } typesize = GDALGetDataTypeSize(eBufType) / 8; if (GDALGetDataTypeSize(eBufType) % 8 > 0) typesize++; eSrcType = this->eSourceTransferType; if ((eSrcType == GDT_Unknown) || (eSrcType >= GDT_TypeCount)) { eSrcType = eBufType; } sourcesize = GDALGetDataTypeSize(eSrcType) / 8; /* -------------------------------------------------------------------- */ /* Initialize the buffer to some background value. Use the */ /* nodata value if available. */ /* -------------------------------------------------------------------- */ if ( nPixelSpace == typesize && (!bNoDataValueSet || dfNoDataValue == 0) ) { memset( pData, 0, nBufXSize * nBufYSize * nPixelSpace ); } else if ( !bEqualAreas || bNoDataValueSet ) { double dfWriteValue = 0.0; int iLine; if( bNoDataValueSet ) dfWriteValue = dfNoDataValue; for( iLine = 0; iLine < nBufYSize; iLine++ ) { GDALCopyWords( &dfWriteValue, GDT_Float64, 0, ((GByte *)pData) + nLineSpace * iLine, eBufType, nPixelSpace, nBufXSize ); } } /* -------------------------------------------------------------------- */ /* 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 ) == CE_None ) return CE_None; } /* ---- Get pixel function for band ---- */ pfnPixelFunc = VRTDerivedRasterBand::GetPixelFunction(this->pszFuncName); if (pfnPixelFunc == NULL) { CPLError( CE_Failure, CPLE_IllegalArg, "VRTDerivedRasterBand::IRasterIO:" \ "Derived band pixel function '%s' not registered.\n", this->pszFuncName); return CE_Failure; } /* TODO: It would be nice to use a MallocBlock function for each individual buffer that would recycle blocks of memory from a cache by reassigning blocks that are nearly the same size. A corresponding FreeBlock might only truly free if the total size of freed blocks gets to be too great of a percentage of the size of the allocated blocks. */ /* ---- Get buffers for each source ---- */ pBuffers = (void **) CPLMalloc(sizeof(void *) * nSources); for (iSource = 0; iSource < nSources; iSource++) { pBuffers[iSource] = (void *) VSIMalloc(sourcesize * nBufXSize * nBufYSize); if (pBuffers[iSource] == NULL) { for (ii = 0; ii < iSource; ii++) { VSIFree(pBuffers[iSource]); } CPLError( CE_Failure, CPLE_OutOfMemory, "VRTDerivedRasterBand::IRasterIO:" \ "Out of memory allocating %d bytes.\n", nPixelSpace * nBufXSize * nBufYSize); return CE_Failure; } /* ------------------------------------------------------------ */ /* #4045: Initialize the newly allocated buffers before handing */ /* them off to the sources. These buffers are packed, so we */ /* don't need any special line-by-line handling when a nonzero */ /* nodata value is set. */ /* ------------------------------------------------------------ */ if ( !bNoDataValueSet || dfNoDataValue == 0 ) { memset( pBuffers[iSource], 0, sourcesize * nBufXSize * nBufYSize ); } else { GDALCopyWords( &dfNoDataValue, GDT_Float64, 0, (GByte *) pBuffers[iSource], eSrcType, sourcesize, nBufXSize * nBufYSize); } } /* ---- Load values for sources into packed buffers ---- */ for(iSource = 0; iSource < nSources; iSource++) { eErr = ((VRTSource *)papoSources[iSource])->RasterIO (nXOff, nYOff, nXSize, nYSize, pBuffers[iSource], nBufXSize, nBufYSize, eSrcType, GDALGetDataTypeSize( eSrcType ) / 8, (GDALGetDataTypeSize( eSrcType ) / 8) * nBufXSize); } /* ---- Apply pixel function ---- */ if (eErr == CE_None) { eErr = pfnPixelFunc((void **)pBuffers, nSources, pData, nBufXSize, nBufYSize, eSrcType, eBufType, nPixelSpace, nLineSpace); } /* ---- Release buffers ---- */ for (iSource = 0; iSource < nSources; iSource++) { VSIFree(pBuffers[iSource]); } CPLFree(pBuffers); return eErr; }
CPLErr VRTSourcedRasterBand::IRasterIO( GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize, void * pData, int nBufXSize, int nBufYSize, GDALDataType eBufType, int nPixelSpace, int nLineSpace ) { int iSource; CPLErr eErr = CE_None; if( eRWFlag == GF_Write ) { CPLError( CE_Failure, CPLE_AppDefined, "Writing through VRTSourcedRasterBand is not supported." ); return CE_Failure; } /* When using GDALProxyPoolDataset for sources, the recusion will not be */ /* detected at VRT opening but when doing RasterIO. As the proxy pool will */ /* return the already opened dataset, we can just test a member variable. */ if ( bAlreadyInIRasterIO ) { CPLError( CE_Failure, CPLE_AppDefined, "VRTSourcedRasterBand::IRasterIO() called recursively on the same band. " "It looks like the VRT is referencing itself." ); 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 ) == CE_None ) return CE_None; } /* -------------------------------------------------------------------- */ /* Initialize the buffer to some background value. Use the */ /* nodata value if available. */ /* -------------------------------------------------------------------- */ if ( nPixelSpace == GDALGetDataTypeSize(eBufType)/8 && (!bNoDataValueSet || (!CPLIsNan(dfNoDataValue) && dfNoDataValue == 0)) ) { if (nLineSpace == nBufXSize * nPixelSpace) { memset( pData, 0, nBufYSize * nLineSpace ); } else { int iLine; for( iLine = 0; iLine < nBufYSize; iLine++ ) { memset( ((GByte*)pData) + iLine * nLineSpace, 0, nBufXSize * nPixelSpace ); } } } else if ( !bEqualAreas || bNoDataValueSet ) { double dfWriteValue = 0.0; int iLine; if( bNoDataValueSet ) dfWriteValue = dfNoDataValue; for( iLine = 0; iLine < nBufYSize; iLine++ ) { GDALCopyWords( &dfWriteValue, GDT_Float64, 0, ((GByte *)pData) + nLineSpace * iLine, eBufType, nPixelSpace, nBufXSize ); } } /* -------------------------------------------------------------------- */ /* 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 ) == CE_None ) return CE_None; } bAlreadyInIRasterIO = TRUE; /* -------------------------------------------------------------------- */ /* Overlay each source in turn over top this. */ /* -------------------------------------------------------------------- */ for( iSource = 0; eErr == CE_None && iSource < nSources; iSource++ ) { eErr = papoSources[iSource]->RasterIO( nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace); } bAlreadyInIRasterIO = FALSE; return eErr; }
CPLErr RawRasterBand::IRasterIO( GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize, void * pData, int nBufXSize, int nBufYSize, GDALDataType eBufType, int nPixelSpace, int nLineSpace ) { int nBandDataSize = GDALGetDataTypeSize(eDataType) / 8; int nBufDataSize = GDALGetDataTypeSize( eBufType ) / 8; int nBytesToRW = nPixelOffset * nXSize; if( !CanUseDirectIO(nXOff, nYOff, nXSize, nYSize, eBufType ) ) { return GDALRasterBand::IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace ); } CPLDebug("RAW", "Using direct IO implementation"); /* ==================================================================== */ /* Read data. */ /* ==================================================================== */ if ( eRWFlag == GF_Read ) { /* -------------------------------------------------------------------- */ /* 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 ) == CE_None ) return CE_None; } /* ==================================================================== */ /* 1. Simplest case when we should get contiguous block */ /* of uninterleaved pixels. */ /* ==================================================================== */ if ( nXSize == GetXSize() && nXSize == nBufXSize && nYSize == nBufYSize && eBufType == eDataType && nPixelOffset == nBandDataSize && nPixelSpace == nBufDataSize && nLineSpace == nPixelSpace * nXSize ) { vsi_l_offset nOffset = nImgOffset + (vsi_l_offset)nYOff * nLineOffset + nXOff; if ( AccessBlock( nOffset, nXSize * nYSize * nBandDataSize, pData ) != CE_None ) { CPLError( CE_Failure, CPLE_FileIO, "Failed to read %d bytes at " CPL_FRMT_GUIB ".", nXSize * nYSize * nBandDataSize, nOffset); } } /* ==================================================================== */ /* 2. Case when we need deinterleave and/or subsample data. */ /* ==================================================================== */ else { GByte *pabyData; double dfSrcXInc, dfSrcYInc; int iLine; dfSrcXInc = (double)nXSize / nBufXSize; dfSrcYInc = (double)nYSize / nBufYSize; pabyData = (GByte *) CPLMalloc( nBytesToRW ); for ( iLine = 0; iLine < nBufYSize; iLine++ ) { vsi_l_offset nOffset = nImgOffset + ((vsi_l_offset)nYOff + (vsi_l_offset)(iLine * dfSrcYInc)) * nLineOffset + nXOff * nPixelOffset; if ( AccessBlock( nOffset, nBytesToRW, pabyData ) != CE_None ) { CPLError( CE_Failure, CPLE_FileIO, "Failed to read %d bytes at " CPL_FRMT_GUIB ".", nBytesToRW, nOffset ); } /* -------------------------------------------------------------------- */ /* Copy data from disk buffer to user block buffer and subsample, */ /* if needed. */ /* -------------------------------------------------------------------- */ if ( nXSize == nBufXSize && nYSize == nBufYSize ) { GDALCopyWords( pabyData, eDataType, nPixelOffset, (GByte *)pData + (vsi_l_offset)iLine * nLineSpace, eBufType, nPixelSpace, nXSize ); } else { int iPixel; for ( iPixel = 0; iPixel < nBufXSize; iPixel++ ) { GDALCopyWords( pabyData + (vsi_l_offset)(iPixel * dfSrcXInc) * nPixelOffset, eDataType, nPixelOffset, (GByte *)pData + (vsi_l_offset)iLine * nLineSpace + (vsi_l_offset)iPixel * nPixelSpace, eBufType, nPixelSpace, 1 ); } } } CPLFree( pabyData ); } } /* ==================================================================== */ /* Write data. */ /* ==================================================================== */ else { int nBytesActuallyWritten; /* ==================================================================== */ /* 1. Simplest case when we should write contiguous block */ /* of uninterleaved pixels. */ /* ==================================================================== */ if ( nXSize == GetXSize() && nXSize == nBufXSize && nYSize == nBufYSize && eBufType == eDataType && nPixelOffset == nBandDataSize && nPixelSpace == nBufDataSize && nLineSpace == nPixelSpace * nXSize ) { /* -------------------------------------------------------------------- */ /* Byte swap the data buffer, if required. */ /* -------------------------------------------------------------------- */ if( !bNativeOrder && eDataType != GDT_Byte ) { if( GDALDataTypeIsComplex( eDataType ) ) { int nWordSize; nWordSize = GDALGetDataTypeSize(eDataType)/16; GDALSwapWords( pData, nWordSize, nXSize, nPixelOffset ); GDALSwapWords( ((GByte *) pData) + nWordSize, nWordSize, nXSize, nPixelOffset ); } else GDALSwapWords( pData, nBandDataSize, nXSize, nPixelOffset ); } /* -------------------------------------------------------------------- */ /* Seek to the right block. */ /* -------------------------------------------------------------------- */ vsi_l_offset nOffset = nImgOffset + (vsi_l_offset)nYOff * nLineOffset + nXOff; if( Seek( nOffset, SEEK_SET) == -1 ) { CPLError( CE_Failure, CPLE_FileIO, "Failed to seek to " CPL_FRMT_GUIB " to write data.\n", nOffset); return CE_Failure; } /* -------------------------------------------------------------------- */ /* Write the block. */ /* -------------------------------------------------------------------- */ nBytesToRW = nXSize * nYSize * nBandDataSize; nBytesActuallyWritten = Write( pData, 1, nBytesToRW ); if( nBytesActuallyWritten < nBytesToRW ) { CPLError( CE_Failure, CPLE_FileIO, "Failed to write %d bytes to file. %d bytes written", nBytesToRW, nBytesActuallyWritten ); return CE_Failure; } /* -------------------------------------------------------------------- */ /* Byte swap (if necessary) back into machine order so the */ /* buffer is still usable for reading purposes. */ /* -------------------------------------------------------------------- */ if( !bNativeOrder && eDataType != GDT_Byte ) { if( GDALDataTypeIsComplex( eDataType ) ) { int nWordSize; nWordSize = GDALGetDataTypeSize(eDataType)/16; GDALSwapWords( pData, nWordSize, nXSize, nPixelOffset ); GDALSwapWords( ((GByte *) pData) + nWordSize, nWordSize, nXSize, nPixelOffset ); } else GDALSwapWords( pData, nBandDataSize, nXSize, nPixelOffset ); } } /* ==================================================================== */ /* 2. Case when we need deinterleave and/or subsample data. */ /* ==================================================================== */ else { GByte *pabyData; double dfSrcXInc, dfSrcYInc; vsi_l_offset nBlockOff; int iLine; dfSrcXInc = (double)nXSize / nBufXSize; dfSrcYInc = (double)nYSize / nBufYSize; pabyData = (GByte *) CPLMalloc( nBytesToRW ); for ( iLine = 0; iLine < nBufYSize; iLine++ ) { nBlockOff = nImgOffset + ((vsi_l_offset)nYOff + (vsi_l_offset)(iLine*dfSrcYInc))*nLineOffset + nXOff * nPixelOffset; /* -------------------------------------------------------------------- */ /* If the data for this band is completely contiguous we don't */ /* have to worry about pre-reading from disk. */ /* -------------------------------------------------------------------- */ if( nPixelOffset > nBandDataSize ) AccessBlock( nBlockOff, nBytesToRW, pabyData ); /* -------------------------------------------------------------------- */ /* Copy data from user block buffer to disk buffer and subsample, */ /* if needed. */ /* -------------------------------------------------------------------- */ if ( nXSize == nBufXSize && nYSize == nBufYSize ) { GDALCopyWords( (GByte *)pData + (vsi_l_offset)iLine * nLineSpace, eBufType, nPixelSpace, pabyData, eDataType, nPixelOffset, nXSize ); } else { int iPixel; for ( iPixel = 0; iPixel < nBufXSize; iPixel++ ) { GDALCopyWords( (GByte *)pData+(vsi_l_offset)iLine*nLineSpace + (vsi_l_offset)iPixel * nPixelSpace, eBufType, nPixelSpace, pabyData + (vsi_l_offset)(iPixel * dfSrcXInc) * nPixelOffset, eDataType, nPixelOffset, 1 ); } } /* -------------------------------------------------------------------- */ /* Byte swap the data buffer, if required. */ /* -------------------------------------------------------------------- */ if( !bNativeOrder && eDataType != GDT_Byte ) { if( GDALDataTypeIsComplex( eDataType ) ) { int nWordSize; nWordSize = GDALGetDataTypeSize(eDataType)/16; GDALSwapWords( pabyData, nWordSize, nXSize, nPixelOffset ); GDALSwapWords( ((GByte *) pabyData) + nWordSize, nWordSize, nXSize, nPixelOffset ); } else GDALSwapWords( pabyData, nBandDataSize, nXSize, nPixelOffset ); } /* -------------------------------------------------------------------- */ /* Seek to the right line in block. */ /* -------------------------------------------------------------------- */ if( Seek( nBlockOff, SEEK_SET) == -1 ) { CPLError( CE_Failure, CPLE_FileIO, "Failed to seek to " CPL_FRMT_GUIB " to read.\n", nBlockOff ); return CE_Failure; } /* -------------------------------------------------------------------- */ /* Write the line of block. */ /* -------------------------------------------------------------------- */ nBytesActuallyWritten = Write( pabyData, 1, nBytesToRW ); if( nBytesActuallyWritten < nBytesToRW ) { CPLError( CE_Failure, CPLE_FileIO, "Failed to write %d bytes to file. %d bytes written", nBytesToRW, nBytesActuallyWritten ); return CE_Failure; } /* -------------------------------------------------------------------- */ /* Byte swap (if necessary) back into machine order so the */ /* buffer is still usable for reading purposes. */ /* -------------------------------------------------------------------- */ if( !bNativeOrder && eDataType != GDT_Byte ) { if( GDALDataTypeIsComplex( eDataType ) ) { int nWordSize; nWordSize = GDALGetDataTypeSize(eDataType)/16; GDALSwapWords( pabyData, nWordSize, nXSize, nPixelOffset ); GDALSwapWords( ((GByte *) pabyData) + nWordSize, nWordSize, nXSize, nPixelOffset ); } else GDALSwapWords( pabyData, nBandDataSize, nXSize, nPixelOffset ); } } bDirty = TRUE; CPLFree( pabyData ); } } return CE_None; }
CPLErr PostGISRasterRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize, void * pData, int nBufXSize, int nBufYSize, GDALDataType eBufType, int nPixelSpace, int nLineSpace) { PostGISRasterDataset * poRDS = (PostGISRasterDataset *)poDS; CPLDebug("PostGIS_Raster", "PostGISRasterRasterBand::IRasterIO(): Table %s.%s (%s), Srid : %d", pszSchema, pszTable, pszColumn, poRDS->nSrid); if (eRWFlag == GF_Write) { /* ReportError(CE_Failure, CPLE_NotSupported, "Writing through PostGIS Raster band not supported yet"); return CE_Failure; */ // Delegate in generic IRasterIO to force IWriteBlock call. // TODO: This is not a solution for UPDATE scenario, but it works // for CREATE scenario. return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace); } /******************************************************************* * Do we have overviews that would be appropriate to satisfy this * request? ******************************************************************/ if( (nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() > 0 && eRWFlag == GF_Read) { if(OverviewRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace) == CE_None) return CE_None; } 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 #ifdef notdef /******************************************************************* * Optimization: We just have one tile. So, we can read it with * IReadBlock * * TODO: Review it. It's not working (see comment in * PostGISRasterDataset::ConstructOneDatasetFromTiles) ******************************************************************/ if (poRDS->nTiles == 1) { return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace); } #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 == NULL) { ReportError(CE_Failure, CPLE_AppDefined, "Could not read metadata index."); return CE_Failure; } NullBuffer(pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, 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 = (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 = (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 != NULL) { if( osIDsToFetch.size() != 0 ) 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 */ int bAllBandCaching = FALSE; if (nTilesToFetch > 0) { GIntBig nCacheMax = (GIntBig) 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 osCommand; PGresult * poResult; CPLString osRasterToFetch; if (bAllBandCaching) osRasterToFetch = pszColumn; else osRasterToFetch.Printf("ST_Band(%s, %d)", pszColumn, nBand); int bHasWhere = FALSE; if (osIDsToFetch.size() && (poRDS->bIsFastPK || !(poRDS->HasSpatialIndex())) ) { osCommand.Printf("SELECT %s, " "ST_Metadata(%s), %s FROM %s.%s", osRasterToFetch.c_str(), pszColumn, poRDS->GetPrimaryKeyRef(), pszSchema, pszTable); if( nTilesToFetch < poRDS->nTiles || poRDS->bBuildQuadTreeDynamically ) { bHasWhere = TRUE; osCommand += " WHERE "; osCommand += poRDS->pszPrimaryKeyName; osCommand += " IN ("; osCommand += osIDsToFetch; osCommand += ")"; } } else { CPLLocaleC oCLocale; // Force C locale to avoid commas instead of decimal points (for QGIS e.g.) bHasWhere = TRUE; osCommand.Printf("SELECT %s, ST_Metadata(%s), %s FROM %s.%s WHERE ", osRasterToFetch.c_str(), pszColumn, (poRDS->GetPrimaryKeyRef()) ? poRDS->GetPrimaryKeyRef() : "'foo'", pszSchema, pszTable); if( poRDS->HasSpatialIndex() ) { osCommand += CPLSPrintf("%s && " "ST_GeomFromText('POLYGON((%.18f %.18f,%.18f %.18f,%.18f %.18f,%.18f %.18f,%.18f %.18f))')", pszColumn, 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 osCommand += CPLSPrintf("ST_UpperLeftX(%s)" " BETWEEN %f AND %f AND ST_UpperLeftY(%s) BETWEEN " "%f AND %f", pszColumn, sAoi.minx-EPS, sAoi.maxx+EPS, pszColumn, sAoi.miny-EPS, sAoi.maxy+EPS); } } if( poRDS->pszWhere != NULL ) { if( bHasWhere ) osCommand += " AND ("; else osCommand += " WHERE ("; osCommand += poRDS->pszWhere; osCommand += ")"; } 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 == NULL || 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* pszMetadata = PQgetvalue(poResult, i, 1); const char* pszRaster = PQgetvalue(poResult, i, 0); const char *pszPKID = (poRDS->GetPrimaryKeyRef() != NULL) ? PQgetvalue(poResult, i, 2) : NULL; 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 determinist */ if( poRDS->GetPrimaryKeyRef() != NULL ) { qsort(papsMatchingTiles, nFeatureCount, sizeof(PostGISRasterTileDataset*), SortTilesByPKID); } for(i = 0; i < nFeatureCount && eErr == CE_None; i++) { PostGISRasterTileDataset *poTile = papsMatchingTiles[i]; PostGISRasterTileRasterBand* poTileBand = (PostGISRasterTileRasterBand *)poTile->GetRasterBand(nBand); eErr = poTileBand->poSource->RasterIO( nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace); } // Free the object that holds pointers to matching tiles CPLFree(papsMatchingTiles); return eErr; }