wxGISFeatureSet* wxGISFeatureDataset::GetFeatureSet(IQueryFilter* pQFilter /* = NULL */, ITrackCancel* pTrackCancel /* = NULL */) { if (m_OGRFeatureArray.size() < GetSize()) { OGRFeature* poFeature; while((poFeature = m_poLayer->GetNextFeature()) != NULL ) { if (pTrackCancel && !pTrackCancel->Continue()) return NULL; AddFeature(poFeature); } } wxGISFeatureSet* pGISFeatureSet = new wxGISFeatureSet(m_OGRFeatureArray.size()); if (pQFilter) { wxGISSpatialFilter* pSpaFil = dynamic_cast<wxGISSpatialFilter*>(pQFilter); if (pSpaFil && m_pQuadTree) { int count(0); OGREnvelope Env = pSpaFil->GetEnvelope(); CPLRectObj Rect = {Env.MinX, Env.MinY, Env.MaxX, Env.MaxY}; OGRFeature** pFeatureArr = (OGRFeature**)CPLQuadTreeSearch(m_pQuadTree, &Rect, &count); for (int i = 0; i < count; i++) { if(pTrackCancel && !pTrackCancel->Continue()) break; pGISFeatureSet->AddFeature(pFeatureArr[i]); } delete [] pFeatureArr; } } else { for (size_t i = 0; i < m_OGRFeatureArray.size(); i++) { if(pTrackCancel && !pTrackCancel->Continue()) break; pGISFeatureSet->AddFeature(m_OGRFeatureArray[i]); } } return pGISFeatureSet; }
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; }