CPLErr GDALRasterBlock::Internalize() { CPLMutexHolderD(&hRBMutex); void *pNewData; int nSizeInBytes; GIntBig nCurCacheMax = GDALGetCacheMax64(); /* No risk of overflow as it is checked in GDALRasterBand::InitBlockInfo() */ nSizeInBytes = nXSize * nYSize * (GDALGetDataTypeSize(eType) / 8); pNewData = VSIMalloc(nSizeInBytes); if (pNewData == NULL) { CPLError(CE_Failure, CPLE_OutOfMemory, "GDALRasterBlock::Internalize : Out of memory allocating %d bytes.", nSizeInBytes); return(CE_Failure); } if (pData != NULL) memcpy(pNewData, pData, nSizeInBytes); pData = pNewData; /* -------------------------------------------------------------------- */ /* Flush old blocks if we are nearing our memory limit. */ /* -------------------------------------------------------------------- */ AddLock(); /* don't flush this block! */ nCacheUsed += nSizeInBytes; while (nCacheUsed > nCurCacheMax) { GIntBig nOldCacheUsed = nCacheUsed; GDALFlushCacheBlock(); if (nCacheUsed == nOldCacheUsed) break; } /* -------------------------------------------------------------------- */ /* Add this block to the list. */ /* -------------------------------------------------------------------- */ Touch(); DropLock(); return(CE_None); }
int CPL_STDCALL GDALGetCacheMax() { GIntBig nRes = GDALGetCacheMax64(); if (nRes > INT_MAX) { static int bHasWarned = FALSE; if (!bHasWarned) { CPLError(CE_Warning, CPLE_AppDefined, "Cache max value doesn't fit on a 32 bit integer. " "Call GDALGetCacheMax64() instead"); bHasWarned = TRUE; } nRes = INT_MAX; } return (int)nRes; }
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; }
CPLErr GDALRasterBlock::Internalize() { void *pNewData = NULL; int nSizeInBytes; CPLAssert( pData == NULL ); // This call will initialize the hRBLock mutex. Other call places can // only be called if we have go through there. GIntBig nCurCacheMax = GDALGetCacheMax64(); /* No risk of overflow as it is checked in GDALRasterBand::InitBlockInfo() */ nSizeInBytes = GetBlockSize(); /* -------------------------------------------------------------------- */ /* Flush old blocks if we are nearing our memory limit. */ /* -------------------------------------------------------------------- */ int bFirstIter = TRUE; int bLoopAgain; do { bLoopAgain = FALSE; GDALRasterBlock* apoBlocksToFree[64]; int nBlocksToFree = 0; { TAKE_LOCK; if( bFirstIter ) nCacheUsed += nSizeInBytes; GDALRasterBlock *poTarget = poOldest; while( nCacheUsed > nCurCacheMax ) { while( poTarget != NULL && poTarget->GetLockCount() > 0 ) poTarget = poTarget->poPrevious; if( poTarget != NULL ) { GDALRasterBlock* _poPrevious = poTarget->poPrevious; poTarget->Detach_unlocked(); poTarget->GetBand()->UnreferenceBlock(poTarget->GetXOff(),poTarget->GetYOff()); apoBlocksToFree[nBlocksToFree++] = poTarget; if( poTarget->GetDirty() ) { // Only free one dirty block at a time so that // other dirty blocks of other bands with the same coordinates // can be found with TryGetLockedBlock() bLoopAgain = ( nCacheUsed > nCurCacheMax ); break; } if( nBlocksToFree == 64 ) { CPLDebug("GDAL", "More than 64 blocks are flagged to be flushed. Not trying more"); break; } poTarget = _poPrevious; } else break; } /* -------------------------------------------------------------------- */ /* Add this block to the list. */ /* -------------------------------------------------------------------- */ if( !bLoopAgain ) Touch_unlocked(); } bFirstIter = FALSE; /* Now free blocks we have detached and removed from their band */ for(int i=0; i<nBlocksToFree; i++) { GDALRasterBlock *poBlock = apoBlocksToFree[i]; if( poBlock->GetDirty() ) { CPLErr eErr = poBlock->Write(); if( eErr != CE_None ) { /* Save the error for later reporting */ poBlock->GetBand()->SetFlushBlockErr(eErr); } } /* Try to recycle the data of an existing block */ void* pDataBlock = poBlock->pData; if( pNewData == NULL && pDataBlock != NULL && poBlock->GetBlockSize() >= nSizeInBytes ) { pNewData = pDataBlock; poBlock->pData = NULL; } delete poBlock; } } while(bLoopAgain); if( pNewData == NULL ) { pNewData = VSIMalloc( nSizeInBytes ); if( pNewData == NULL ) { CPLError( CE_Failure, CPLE_OutOfMemory, "GDALRasterBlock::Internalize : Out of memory allocating %d bytes.", nSizeInBytes); return( CE_Failure ); } } pData = pNewData; return( CE_None ); }
CPLErr GDALRasterBlock::Internalize() { CPLAssert( pData == NULL ); void *pNewData = NULL; // This call will initialize the hRBLock mutex. Other call places can // only be called if we have go through there. const GIntBig nCurCacheMax = GDALGetCacheMax64(); // No risk of overflow as it is checked in GDALRasterBand::InitBlockInfo(). const int nSizeInBytes = GetBlockSize(); /* -------------------------------------------------------------------- */ /* Flush old blocks if we are nearing our memory limit. */ /* -------------------------------------------------------------------- */ bool bFirstIter = true; bool bLoopAgain = false; do { bLoopAgain = false; GDALRasterBlock* apoBlocksToFree[64] = { NULL }; int nBlocksToFree = 0; { TAKE_LOCK; if( bFirstIter ) nCacheUsed += nSizeInBytes; GDALRasterBlock *poTarget = poOldest; while( nCacheUsed > nCurCacheMax ) { while( poTarget != NULL ) { if( CPLAtomicCompareAndExchange( &(poTarget->nLockCount), 0, -1) ) break; poTarget = poTarget->poPrevious; } if( poTarget != NULL ) { if( bSleepsForBockCacheDebug ) CPLSleep(CPLAtof( CPLGetConfigOption( "GDAL_RB_INTERNALIZE_SLEEP_AFTER_DROP_LOCK", "0"))); GDALRasterBlock* _poPrevious = poTarget->poPrevious; poTarget->Detach_unlocked(); poTarget->GetBand()->UnreferenceBlock(poTarget); apoBlocksToFree[nBlocksToFree++] = poTarget; if( poTarget->GetDirty() ) { // Only free one dirty block at a time so that // other dirty blocks of other bands with the same // coordinates can be found with TryGetLockedBlock() bLoopAgain = nCacheUsed > nCurCacheMax; break; } if( nBlocksToFree == 64 ) { bLoopAgain = ( nCacheUsed > nCurCacheMax ); break; } poTarget = _poPrevious; } else { break; } } /* ------------------------------------------------------------------ */ /* Add this block to the list. */ /* ------------------------------------------------------------------ */ if( !bLoopAgain ) Touch_unlocked(); } bFirstIter = false; // Now free blocks we have detached and removed from their band. for( int i = 0; i < nBlocksToFree; ++i) { GDALRasterBlock * const poBlock = apoBlocksToFree[i]; if( poBlock->GetDirty() ) { CPLErr eErr = poBlock->Write(); if( eErr != CE_None ) { // Save the error for later reporting. poBlock->GetBand()->SetFlushBlockErr(eErr); } } // Try to recycle the data of an existing block. void* pDataBlock = poBlock->pData; if( pNewData == NULL && pDataBlock != NULL && poBlock->GetBlockSize() == nSizeInBytes ) { pNewData = pDataBlock; } else { VSIFree(poBlock->pData); } poBlock->pData = NULL; poBlock->GetBand()->AddBlockToFreeList(poBlock); } } while(bLoopAgain); if( pNewData == NULL ) { pNewData = VSI_MALLOC_VERBOSE( nSizeInBytes ); if( pNewData == NULL ) { return( CE_Failure ); } } pData = pNewData; return CE_None; }
CPLErr GDALRasterizeLayers(GDALDatasetH hDS, int nBandCount, int *panBandList, int nLayerCount, OGRLayerH *pahLayers, GDALTransformerFunc pfnTransformer, void *pTransformArg, double *padfLayerBurnValues, char **papszOptions, GDALProgressFunc pfnProgress, void *pProgressArg) { #ifndef OGR_ENABLED CPLError(CE_Failure, CPLE_NotSupported, "GDALRasterizeLayers() unimplemented in a non OGR build"); return CE_Failure; #else GDALDataType eType; unsigned char *pabyChunkBuf; GDALDataset *poDS = (GDALDataset*) hDS; if (pfnProgress == NULL) pfnProgress = GDALDummyProgress; /* -------------------------------------------------------------------- */ /* Do some rudimentary arg checking. */ /* -------------------------------------------------------------------- */ if (nBandCount == 0 || nLayerCount == 0) return CE_None; // prototype band. GDALRasterBand *poBand = poDS->GetRasterBand(panBandList[0]); if (poBand == NULL) return CE_Failure; int bAllTouched = CSLFetchBoolean(papszOptions, "ALL_TOUCHED", FALSE); const char *pszOpt = CSLFetchNameValue(papszOptions, "BURN_VALUE_FROM"); GDALBurnValueSrc eBurnValueSource = GBV_UserBurnValue; if (pszOpt) { if (EQUAL(pszOpt, "Z")) eBurnValueSource = GBV_Z; /*else if( EQUAL(pszOpt,"M")) eBurnValueSource = GBV_M;*/ } /* -------------------------------------------------------------------- */ /* Establish a chunksize to operate on. The larger the chunk */ /* size the less times we need to make a pass through all the */ /* shapes. */ /* -------------------------------------------------------------------- */ int nYChunkSize, nScanlineBytes; const char *pszYChunkSize = CSLFetchNameValue(papszOptions, "CHUNKYSIZE"); if (poBand->GetRasterDataType() == GDT_Byte) eType = GDT_Byte; else eType = GDT_Float32; nScanlineBytes = nBandCount * poDS->GetRasterXSize() * (GDALGetDataTypeSize(eType) / 8); if (pszYChunkSize && ((nYChunkSize = atoi(pszYChunkSize))) != 0) ; else { GIntBig nYChunkSize64 = GDALGetCacheMax64() / nScanlineBytes; if (nYChunkSize64 > INT_MAX) nYChunkSize = INT_MAX; else nYChunkSize = (int)nYChunkSize64; } if (nYChunkSize < 1) nYChunkSize = 1; if (nYChunkSize > poDS->GetRasterYSize()) nYChunkSize = poDS->GetRasterYSize(); pabyChunkBuf = (unsigned char*) VSIMalloc(nYChunkSize * nScanlineBytes); if (pabyChunkBuf == NULL) { CPLError(CE_Failure, CPLE_OutOfMemory, "Unable to allocate rasterization buffer."); return CE_Failure; } /* -------------------------------------------------------------------- */ /* Read the image once for all layers if user requested to render */ /* the whole raster in single chunk. */ /* -------------------------------------------------------------------- */ if (nYChunkSize == poDS->GetRasterYSize()) { if (poDS->RasterIO(GF_Read, 0, 0, poDS->GetRasterXSize(), nYChunkSize, pabyChunkBuf, poDS->GetRasterXSize(), nYChunkSize, eType, nBandCount, panBandList, 0, 0, 0) != CE_None) { CPLError(CE_Failure, CPLE_OutOfMemory, "Unable to read buffer."); CPLFree(pabyChunkBuf); return CE_Failure; } } /* ==================================================================== */ /* Read the specified layers transfoming and rasterizing */ /* geometries. */ /* ==================================================================== */ CPLErr eErr = CE_None; int iLayer; const char *pszBurnAttribute = CSLFetchNameValue(papszOptions, "ATTRIBUTE"); pfnProgress(0.0, NULL, pProgressArg); for (iLayer = 0; iLayer < nLayerCount; iLayer++) { int iBurnField = -1; double *padfBurnValues = NULL; OGRLayer *poLayer = (OGRLayer*) pahLayers[iLayer]; if (!poLayer) { CPLError(CE_Warning, CPLE_AppDefined, "Layer element number %d is NULL, skipping.\n", iLayer); continue; } /* -------------------------------------------------------------------- */ /* If the layer does not contain any features just skip it. */ /* Do not force the feature count, so if driver doesn't know */ /* exact number of features, go down the normal way. */ /* -------------------------------------------------------------------- */ if (poLayer->GetFeatureCount(FALSE) == 0) continue; if (pszBurnAttribute) { iBurnField = poLayer->GetLayerDefn()->GetFieldIndex(pszBurnAttribute); if (iBurnField == -1) { CPLError(CE_Warning, CPLE_AppDefined, "Failed to find field %s on layer %s, skipping.\n", pszBurnAttribute, poLayer->GetLayerDefn()->GetName()); continue; } } else padfBurnValues = padfLayerBurnValues + iLayer * nBandCount; /* -------------------------------------------------------------------- */ /* If we have no transformer, create the one from input file */ /* projection. Note that each layer can be georefernced */ /* separately. */ /* -------------------------------------------------------------------- */ int bNeedToFreeTransformer = FALSE; if (pfnTransformer == NULL) { char *pszProjection = NULL; bNeedToFreeTransformer = TRUE; OGRSpatialReference *poSRS = poLayer->GetSpatialRef(); if (!poSRS) { CPLError(CE_Warning, CPLE_AppDefined, "Failed to fetch spatial reference on layer %s " "to build transformer, assuming matching coordinate systems.\n", poLayer->GetLayerDefn()->GetName()); } else poSRS->exportToWkt(&pszProjection); pTransformArg = GDALCreateGenImgProjTransformer(NULL, pszProjection, hDS, NULL, FALSE, 0.0, 0); pfnTransformer = GDALGenImgProjTransform; CPLFree(pszProjection); } OGRFeature *poFeat; poLayer->ResetReading(); /* -------------------------------------------------------------------- */ /* Loop over image in designated chunks. */ /* -------------------------------------------------------------------- */ int iY; for (iY = 0; iY < poDS->GetRasterYSize() && eErr == CE_None; iY += nYChunkSize) { int nThisYChunkSize; nThisYChunkSize = nYChunkSize; if (nThisYChunkSize + iY > poDS->GetRasterYSize()) nThisYChunkSize = poDS->GetRasterYSize() - iY; // Only re-read image if not a single chunk is being rendered if (nYChunkSize < poDS->GetRasterYSize()) { eErr = poDS->RasterIO(GF_Read, 0, iY, poDS->GetRasterXSize(), nThisYChunkSize, pabyChunkBuf, poDS->GetRasterXSize(), nThisYChunkSize, eType, nBandCount, panBandList, 0, 0, 0); if (eErr != CE_None) break; } double *padfAttrValues = (double*) VSIMalloc(sizeof(double) * nBandCount); while ((poFeat = poLayer->GetNextFeature()) != NULL) { OGRGeometry *poGeom = poFeat->GetGeometryRef(); if (pszBurnAttribute) { int iBand; double dfAttrValue; dfAttrValue = poFeat->GetFieldAsDouble(iBurnField); for (iBand = 0; iBand < nBandCount; iBand++) padfAttrValues[iBand] = dfAttrValue; padfBurnValues = padfAttrValues; } gv_rasterize_one_shape(pabyChunkBuf, iY, poDS->GetRasterXSize(), nThisYChunkSize, nBandCount, eType, bAllTouched, poGeom, padfBurnValues, eBurnValueSource, pfnTransformer, pTransformArg); delete poFeat; } VSIFree(padfAttrValues); // Only write image if not a single chunk is being rendered if (nYChunkSize < poDS->GetRasterYSize()) { eErr = poDS->RasterIO(GF_Write, 0, iY, poDS->GetRasterXSize(), nThisYChunkSize, pabyChunkBuf, poDS->GetRasterXSize(), nThisYChunkSize, eType, nBandCount, panBandList, 0, 0, 0); } poLayer->ResetReading(); if (!pfnProgress((iY + nThisYChunkSize) / ((double)poDS->GetRasterYSize()), "", pProgressArg)) { CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); eErr = CE_Failure; } } if (bNeedToFreeTransformer) { GDALDestroyTransformer(pTransformArg); pTransformArg = NULL; pfnTransformer = NULL; } } /* -------------------------------------------------------------------- */ /* Write out the image once for all layers if user requested */ /* to render the whole raster in single chunk. */ /* -------------------------------------------------------------------- */ if (nYChunkSize == poDS->GetRasterYSize()) { poDS->RasterIO(GF_Write, 0, 0, poDS->GetRasterXSize(), nYChunkSize, pabyChunkBuf, poDS->GetRasterXSize(), nYChunkSize, eType, nBandCount, panBandList, 0, 0, 0); } /* -------------------------------------------------------------------- */ /* cleanup */ /* -------------------------------------------------------------------- */ VSIFree(pabyChunkBuf); return eErr; #endif /* def OGR_ENABLED */ }