Line data Source code
1 : /***********************************************************************
2 : * File : postgisrasterrasterband.cpp
3 : * Project: PostGIS Raster driver
4 : * Purpose: GDAL RasterBand implementation for PostGIS Raster driver
5 : * Author: Jorge Arevalo, jorge.arevalo@deimos-space.com
6 : * jorgearevalo@libregis.org
7 : *
8 : * Author: David Zwarg, dzwarg@azavea.com
9 : *
10 : *
11 : ***********************************************************************
12 : * Copyright (c) 2009 - 2013, Jorge Arevalo, David Zwarg
13 : * Copyright (c) 2013-2018, Even Rouault <even.rouault at spatialys.com>
14 : *
15 : * SPDX-License-Identifier: MIT
16 : **********************************************************************/
17 : #include "postgisraster.h"
18 :
19 : /**
20 : * \brief Constructor.
21 : *
22 : * nBand it is just necessary for overview band creation
23 : */
24 0 : PostGISRasterRasterBand::PostGISRasterRasterBand(PostGISRasterDataset *poDSIn,
25 : int nBandIn,
26 : GDALDataType eDataTypeIn,
27 : GBool bNoDataValueSetIn,
28 0 : double dfNodata)
29 0 : : VRTSourcedRasterBand(poDSIn, nBandIn), pszSchema(poDSIn->pszSchema),
30 0 : pszTable(poDSIn->pszTable), pszColumn(poDSIn->pszColumn)
31 : {
32 : /* Basic properties */
33 0 : poDS = poDSIn;
34 0 : nBand = nBandIn;
35 :
36 0 : eDataType = eDataTypeIn;
37 0 : m_bNoDataValueSet = bNoDataValueSetIn;
38 0 : m_dfNoDataValue = dfNodata;
39 :
40 0 : nRasterXSize = poDS->GetRasterXSize();
41 0 : nRasterYSize = poDS->GetRasterYSize();
42 :
43 : /*******************************************************************
44 : * Finally, set the block size. We apply the same logic than in VRT
45 : * driver.
46 : *
47 : * We limit the size of a block with MAX_BLOCK_SIZE here to prevent
48 : * arrangements of just one big tile.
49 : *
50 : * This value is just used in case we only have 1 tile in the
51 : * table. Otherwise, the reading operations are performed by the
52 : * sources, not the PostGISRasterBand object itself.
53 : ******************************************************************/
54 0 : nBlockXSize = atoi(CPLGetConfigOption(
55 : "PR_BLOCKXSIZE",
56 0 : CPLSPrintf("%d", MIN(MAX_BLOCK_SIZE, this->nRasterXSize))));
57 0 : nBlockYSize = atoi(CPLGetConfigOption(
58 : "PR_BLOCKYSIZE",
59 0 : CPLSPrintf("%d", MIN(MAX_BLOCK_SIZE, this->nRasterYSize))));
60 :
61 : #ifdef DEBUG_VERBOSE
62 : CPLDebug("PostGIS_Raster",
63 : "PostGISRasterRasterBand constructor: Band size: (%d X %d)",
64 : nRasterXSize, nRasterYSize);
65 :
66 : CPLDebug("PostGIS_Raster",
67 : "PostGISRasterRasterBand::Constructor: "
68 : "Block size (%dx%d)",
69 : this->nBlockXSize, this->nBlockYSize);
70 : #endif
71 0 : }
72 :
73 : /***********************************************
74 : * \brief: Band destructor
75 : ***********************************************/
76 0 : PostGISRasterRasterBand::~PostGISRasterRasterBand()
77 : {
78 0 : }
79 :
80 : /********************************************************
81 : * \brief Set nodata value to a buffer
82 : ********************************************************/
83 0 : void PostGISRasterRasterBand::NullBuffer(void *pData, int nBufXSize,
84 : int nBufYSize, GDALDataType eBufType,
85 : int nPixelSpace, int nLineSpace)
86 : {
87 : int j;
88 0 : for (j = 0; j < nBufYSize; j++)
89 : {
90 0 : double dfVal = 0.0;
91 0 : if (m_bNoDataValueSet)
92 0 : dfVal = m_dfNoDataValue;
93 0 : GDALCopyWords(&dfVal, GDT_Float64, 0,
94 0 : static_cast<GByte *>(pData) + j * nLineSpace, eBufType,
95 : nPixelSpace, nBufXSize);
96 : }
97 0 : }
98 :
99 : /********************************************************
100 : * \brief SortTilesByPKID
101 : ********************************************************/
102 0 : static int SortTilesByPKID(const void *a, const void *b)
103 : {
104 0 : const PostGISRasterTileDataset *pa =
105 : *static_cast<const PostGISRasterTileDataset *const *>(a);
106 0 : const PostGISRasterTileDataset *pb =
107 : *static_cast<const PostGISRasterTileDataset *const *>(b);
108 0 : return strcmp(pa->GetPKID(), pb->GetPKID());
109 : }
110 :
111 : /**
112 : * Read/write a region of image data for this band.
113 : *
114 : * This method allows reading a region of a PostGISRasterBand into a buffer.
115 : * The write support is still under development
116 : *
117 : * The function fetches all the raster data that intersects with the region
118 : * provided, and store the data in the GDAL cache.
119 : *
120 : * It automatically takes care of data type translation if the data type
121 : * (eBufType) of the buffer is different than that of the
122 : * PostGISRasterRasterBand.
123 : *
124 : * The nPixelSpace and nLineSpace parameters allow reading into FROM various
125 : * organization of buffers.
126 : *
127 : * @param eRWFlag Either GF_Read to read a region of data (GF_Write, to write
128 : * a region of data, yet not supported)
129 : *
130 : * @param nXOff The pixel offset to the top left corner of the region of the
131 : * band to be accessed. This would be zero to start FROM the left side.
132 : *
133 : * @param nYOff The line offset to the top left corner of the region of the band
134 : * to be accessed. This would be zero to start FROM the top.
135 : *
136 : * @param nXSize The width of the region of the band to be accessed in pixels.
137 : *
138 : * @param nYSize The height of the region of the band to be accessed in lines.
139 : *
140 : * @param pData The buffer into which the data should be read, or FROM which it
141 : * should be written. This buffer must contain at least
142 : * nBufXSize * nBufYSize * nBandCount words of type eBufType. It is organized in
143 : * left to right,top to bottom pixel order. Spacing is controlled by the
144 : * nPixelSpace, and nLineSpace parameters.
145 : *
146 : * @param nBufXSize the width of the buffer image into which the desired region
147 : * is to be read, or FROM which it is to be written.
148 : *
149 : * @param nBufYSize the height of the buffer image into which the desired region
150 : * is to be read, or FROM which it is to be written.
151 : *
152 : * @param eBufType the type of the pixel values in the pData data buffer. The
153 : * pixel values will automatically be translated to/FROM the
154 : * PostGISRasterRasterBand data type as needed.
155 : *
156 : * @param nPixelSpace The byte offset FROM the start of one pixel value in pData
157 : * to the start of the next pixel value within a scanline. If defaulted (0) the
158 : * size of the datatype eBufType is used.
159 : *
160 : * @param nLineSpace The byte offset FROM the start of one scanline in pData to
161 : * the start of the next. If defaulted (0) the size of the datatype
162 : * eBufType * nBufXSize is used.
163 : *
164 : * @return CE_Failure if the access fails, otherwise CE_None.
165 : */
166 :
167 0 : CPLErr PostGISRasterRasterBand::IRasterIO(
168 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
169 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
170 : GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
171 : {
172 : /**
173 : * TODO: Write support not implemented yet
174 : **/
175 0 : if (eRWFlag == GF_Write)
176 : {
177 0 : ReportError(CE_Failure, CPLE_NotSupported,
178 : "Writing through PostGIS Raster band not supported yet");
179 :
180 0 : return CE_Failure;
181 : }
182 :
183 : /*******************************************************************
184 : * Do we have overviews that would be appropriate to satisfy this
185 : * request?
186 : ******************************************************************/
187 0 : if ((nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() > 0)
188 : {
189 0 : if (OverviewRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
190 : nBufXSize, nBufYSize, eBufType, nPixelSpace,
191 0 : nLineSpace, psExtraArg) == CE_None)
192 :
193 0 : return CE_None;
194 : }
195 :
196 0 : PostGISRasterDataset *poRDS = cpl::down_cast<PostGISRasterDataset *>(poDS);
197 :
198 0 : int bSameWindowAsOtherBand =
199 0 : (nXOff == poRDS->nXOffPrev && nYOff == poRDS->nYOffPrev &&
200 0 : nXSize == poRDS->nXSizePrev && nYSize == poRDS->nYSizePrev);
201 0 : poRDS->nXOffPrev = nXOff;
202 0 : poRDS->nYOffPrev = nYOff;
203 0 : poRDS->nXSizePrev = nXSize;
204 0 : poRDS->nYSizePrev = nYSize;
205 :
206 : /* Logic to determine if bands are read in order 1, 2, ... N */
207 : /* If so, then use multi-band caching, otherwise do just single band caching
208 : */
209 0 : if (poRDS->bAssumeMultiBandReadPattern)
210 : {
211 0 : if (nBand != poRDS->nNextExpectedBand)
212 : {
213 0 : CPLDebug("PostGIS_Raster", "Disabling multi-band caching since "
214 : "band access pattern does not match");
215 0 : poRDS->bAssumeMultiBandReadPattern = false;
216 0 : poRDS->nNextExpectedBand = 1;
217 : }
218 : else
219 : {
220 0 : poRDS->nNextExpectedBand++;
221 0 : if (poRDS->nNextExpectedBand > poRDS->GetRasterCount())
222 0 : poRDS->nNextExpectedBand = 1;
223 : }
224 : }
225 : else
226 : {
227 0 : if (nBand == poRDS->nNextExpectedBand)
228 : {
229 0 : poRDS->nNextExpectedBand++;
230 0 : if (poRDS->nNextExpectedBand > poRDS->GetRasterCount())
231 : {
232 0 : CPLDebug("PostGIS_Raster", "Re-enabling multi-band caching");
233 0 : poRDS->bAssumeMultiBandReadPattern = true;
234 0 : poRDS->nNextExpectedBand = 1;
235 : }
236 : }
237 : }
238 :
239 : #ifdef DEBUG_VERBOSE
240 : CPLDebug("PostGIS_Raster",
241 : "PostGISRasterRasterBand::IRasterIO: "
242 : "nBand = %d, nXOff = %d, nYOff = %d, nXSize = %d, nYSize = %d, "
243 : "nBufXSize = %d, nBufYSize = %d",
244 : nBand, nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize);
245 : #endif
246 :
247 : /*******************************************************************
248 : * Several tiles: we first look in all our sources caches. Missing
249 : * blocks are queried
250 : ******************************************************************/
251 : double adfProjWin[8];
252 0 : int nFeatureCount = 0;
253 : CPLRectObj sAoi;
254 :
255 0 : poRDS->PolygonFromCoords(nXOff, nYOff, nXOff + nXSize, nYOff + nYSize,
256 : adfProjWin);
257 : // (p[6], p[7]) is the minimum (x, y), and (p[2], p[3]) the max
258 0 : sAoi.minx = adfProjWin[6];
259 0 : sAoi.maxx = adfProjWin[2];
260 0 : if (adfProjWin[7] < adfProjWin[3])
261 : {
262 0 : sAoi.miny = adfProjWin[7];
263 0 : sAoi.maxy = adfProjWin[3];
264 : }
265 : else
266 : {
267 0 : sAoi.maxy = adfProjWin[7];
268 0 : sAoi.miny = adfProjWin[3];
269 : }
270 :
271 : #ifdef DEBUG_VERBOSE
272 : CPLDebug("PostGIS_Raster",
273 : "PostGISRasterRasterBand::IRasterIO: "
274 : "Intersection box: (%f, %f) - (%f, %f)",
275 : sAoi.minx, sAoi.miny, sAoi.maxx, sAoi.maxy);
276 : #endif
277 :
278 0 : if (poRDS->hQuadTree == nullptr)
279 : {
280 0 : ReportError(CE_Failure, CPLE_AppDefined,
281 : "Could not read metadata index.");
282 0 : return CE_Failure;
283 : }
284 :
285 0 : NullBuffer(pData, nBufXSize, nBufYSize, eBufType,
286 : static_cast<int>(nPixelSpace), static_cast<int>(nLineSpace));
287 :
288 0 : if (poRDS->bBuildQuadTreeDynamically && !bSameWindowAsOtherBand)
289 : {
290 0 : if (!(poRDS->LoadSources(nXOff, nYOff, nXSize, nYSize, nBand)))
291 0 : return CE_Failure;
292 : }
293 :
294 : // Matching sources, to avoid a dumb for loop over the sources
295 : PostGISRasterTileDataset **papsMatchingTiles =
296 : reinterpret_cast<PostGISRasterTileDataset **>(
297 0 : CPLQuadTreeSearch(poRDS->hQuadTree, &sAoi, &nFeatureCount));
298 :
299 : // No blocks found. This is not an error (the raster may have holes)
300 0 : if (nFeatureCount == 0)
301 : {
302 0 : CPLFree(papsMatchingTiles);
303 :
304 0 : return CE_None;
305 : }
306 :
307 : int i;
308 :
309 : /**
310 : * We need to store the max, min coords for the missing tiles in
311 : * any place. This is as good as any other
312 : **/
313 0 : sAoi.minx = 0.0;
314 0 : sAoi.miny = 0.0;
315 0 : sAoi.maxx = 0.0;
316 0 : sAoi.maxy = 0.0;
317 :
318 0 : GIntBig nMemoryRequiredForTiles = 0;
319 0 : CPLString osIDsToFetch;
320 0 : int nTilesToFetch = 0;
321 0 : int nBandDataTypeSize = GDALGetDataTypeSize(eDataType) / 8;
322 :
323 : // Loop just over the intersecting sources
324 0 : for (i = 0; i < nFeatureCount; i++)
325 : {
326 0 : PostGISRasterTileDataset *poTile = papsMatchingTiles[i];
327 : PostGISRasterTileRasterBand *poTileBand =
328 0 : cpl::down_cast<PostGISRasterTileRasterBand *>(
329 : poTile->GetRasterBand(nBand));
330 :
331 0 : nMemoryRequiredForTiles +=
332 0 : static_cast<GIntBig>(poTileBand->GetXSize()) *
333 0 : poTileBand->GetYSize() * nBandDataTypeSize;
334 :
335 : // Missing tile: we'll need to query for it
336 0 : if (!poTileBand->IsCached())
337 : {
338 :
339 : // If we have a PKID, add the tile PKID to the list
340 0 : if (poTile->pszPKID != nullptr)
341 : {
342 0 : if (!osIDsToFetch.empty())
343 0 : osIDsToFetch += ",";
344 0 : osIDsToFetch += "'";
345 0 : osIDsToFetch += poTile->pszPKID;
346 0 : osIDsToFetch += "'";
347 : }
348 :
349 : double dfTileMinX, dfTileMinY, dfTileMaxX, dfTileMaxY;
350 0 : poTile->GetExtent(&dfTileMinX, &dfTileMinY, &dfTileMaxX,
351 : &dfTileMaxY);
352 :
353 : /**
354 : * We keep the general max and min values of all the missing
355 : * tiles, to raise a query that intersect just that area.
356 : *
357 : * TODO: In case of just a few tiles and very separated,
358 : * this strategy is clearly suboptimal. We'll get our
359 : * missing tiles, but with a lot of other not needed tiles.
360 : *
361 : * A possible optimization will be to simply rely on the
362 : * I/O method of the source (must be implemented), in case
363 : * we have minus than a reasonable amount of tiles missing.
364 : * Another criteria to decide would be how separated the
365 : * tiles are. Two queries for just two adjacent tiles is
366 : * also a dumb strategy.
367 : **/
368 0 : if (nTilesToFetch == 0)
369 : {
370 0 : sAoi.minx = dfTileMinX;
371 0 : sAoi.miny = dfTileMinY;
372 0 : sAoi.maxx = dfTileMaxX;
373 0 : sAoi.maxy = dfTileMaxY;
374 : }
375 : else
376 : {
377 0 : if (dfTileMinX < sAoi.minx)
378 0 : sAoi.minx = dfTileMinX;
379 :
380 0 : if (dfTileMinY < sAoi.miny)
381 0 : sAoi.miny = dfTileMinY;
382 :
383 0 : if (dfTileMaxX > sAoi.maxx)
384 0 : sAoi.maxx = dfTileMaxX;
385 :
386 0 : if (dfTileMaxY > sAoi.maxy)
387 0 : sAoi.maxy = dfTileMaxY;
388 : }
389 :
390 0 : nTilesToFetch++;
391 : }
392 : }
393 :
394 : /* Determine caching strategy */
395 0 : bool bAllBandCaching = false;
396 0 : if (nTilesToFetch > 0)
397 : {
398 0 : GIntBig nCacheMax = GDALGetCacheMax64();
399 0 : if (nMemoryRequiredForTiles > nCacheMax)
400 : {
401 0 : CPLDebug("PostGIS_Raster",
402 : "For best performance, the block cache should be able to "
403 : "store " CPL_FRMT_GIB
404 : " bytes for the tiles of the requested window, "
405 : "but it is only " CPL_FRMT_GIB " byte large",
406 : nMemoryRequiredForTiles, nCacheMax);
407 0 : nTilesToFetch = 0;
408 : }
409 :
410 0 : if (poRDS->GetRasterCount() > 1 && poRDS->bAssumeMultiBandReadPattern)
411 : {
412 : GIntBig nMemoryRequiredForTilesAllBands =
413 0 : nMemoryRequiredForTiles * poRDS->GetRasterCount();
414 0 : if (nMemoryRequiredForTilesAllBands <= nCacheMax)
415 : {
416 0 : bAllBandCaching = true;
417 : }
418 : else
419 : {
420 0 : CPLDebug("PostGIS_Raster",
421 : "Caching only this band, but not all bands. "
422 : "Cache should be " CPL_FRMT_GIB " byte large for that",
423 : nMemoryRequiredForTilesAllBands);
424 : }
425 : }
426 : }
427 :
428 : // Raise a query for missing tiles and cache them
429 0 : if (nTilesToFetch > 0)
430 : {
431 :
432 : /**
433 : * There are several options here, to raise the query.
434 : * - Get all the tiles which PKID is in a list of missing
435 : * PKIDs.
436 : * - Get all the tiles that intersect a polygon constructed
437 : * based on the (min - max) values calculated before.
438 : * - Get all the tiles with upper left pixel included in the
439 : * range (min - max) calculated before.
440 : *
441 : * The first option is the most efficient one when a PKID exists.
442 : * After that, the second one is the most efficient one when a
443 : * spatial index exists.
444 : * The third one is the only one available when neither a PKID or
445 : *spatial index exist.
446 : **/
447 :
448 0 : CPLString osSchemaI(CPLQuotedSQLIdentifier(pszSchema));
449 0 : CPLString osTableI(CPLQuotedSQLIdentifier(pszTable));
450 0 : CPLString osColumnI(CPLQuotedSQLIdentifier(pszColumn));
451 :
452 0 : CPLString osWHERE;
453 0 : if (!osIDsToFetch.empty() &&
454 0 : (poRDS->bIsFastPK || !(poRDS->HasSpatialIndex())))
455 : {
456 0 : if (nTilesToFetch < poRDS->m_nTiles ||
457 0 : poRDS->bBuildQuadTreeDynamically)
458 : {
459 0 : osWHERE += poRDS->pszPrimaryKeyName;
460 0 : osWHERE += " IN (";
461 0 : osWHERE += osIDsToFetch;
462 0 : osWHERE += ")";
463 : }
464 : }
465 : else
466 : {
467 0 : if (poRDS->HasSpatialIndex())
468 : {
469 : osWHERE += CPLSPrintf(
470 : "%s && "
471 : "ST_GeomFromText('POLYGON((%.18f %.18f,%.18f %.18f,%.18f "
472 : "%.18f,%.18f %.18f,%.18f %.18f))')",
473 : osColumnI.c_str(), adfProjWin[0], adfProjWin[1],
474 : adfProjWin[2], adfProjWin[3], adfProjWin[4], adfProjWin[5],
475 0 : adfProjWin[6], adfProjWin[7], adfProjWin[0], adfProjWin[1]);
476 : }
477 : else
478 : {
479 : #define EPS 1e-5
480 : osWHERE += CPLSPrintf(
481 : "ST_UpperLeftX(%s)"
482 : " BETWEEN %f AND %f AND ST_UpperLeftY(%s) BETWEEN "
483 : "%f AND %f",
484 0 : osColumnI.c_str(), sAoi.minx - EPS, sAoi.maxx + EPS,
485 0 : osColumnI.c_str(), sAoi.miny - EPS, sAoi.maxy + EPS);
486 : }
487 : }
488 :
489 0 : if (poRDS->pszWhere != nullptr)
490 : {
491 0 : if (!osWHERE.empty())
492 0 : osWHERE += " AND ";
493 0 : osWHERE += "(";
494 0 : osWHERE += poRDS->pszWhere;
495 0 : osWHERE += ")";
496 : }
497 :
498 0 : bool bCanUseClientSide = true;
499 0 : if (poRDS->eOutDBResolution == OutDBResolution::CLIENT_SIDE_IF_POSSIBLE)
500 : {
501 : bCanUseClientSide =
502 0 : poRDS->CanUseClientSideOutDB(bAllBandCaching, nBand, osWHERE);
503 : }
504 :
505 0 : CPLString osRasterToFetch;
506 0 : if (bAllBandCaching)
507 0 : osRasterToFetch = osColumnI;
508 : else
509 0 : osRasterToFetch.Printf("ST_Band(%s, %d)", osColumnI.c_str(), nBand);
510 0 : if (poRDS->eOutDBResolution == OutDBResolution::SERVER_SIDE ||
511 0 : !bCanUseClientSide)
512 : {
513 : osRasterToFetch =
514 0 : "encode(ST_AsBinary(" + osRasterToFetch + ",TRUE),'hex')";
515 : }
516 :
517 0 : CPLString osCommand;
518 : osCommand.Printf("SELECT %s, ST_Metadata(%s), %s FROM %s.%s",
519 0 : (poRDS->GetPrimaryKeyRef()) ? poRDS->GetPrimaryKeyRef()
520 : : "NULL",
521 : osColumnI.c_str(), osRasterToFetch.c_str(),
522 0 : osSchemaI.c_str(), osTableI.c_str());
523 0 : if (!osWHERE.empty())
524 : {
525 0 : osCommand += " WHERE " + osWHERE;
526 : }
527 :
528 0 : PGresult *poResult = PQexec(poRDS->poConn, osCommand.c_str());
529 :
530 : #ifdef DEBUG_QUERY
531 : CPLDebug("PostGIS_Raster",
532 : "PostGISRasterRasterBand::IRasterIO(): Query = \"%s\" --> "
533 : "number of rows = %d",
534 : osCommand.c_str(), poResult ? PQntuples(poResult) : 0);
535 : #endif
536 :
537 0 : if (poResult == nullptr ||
538 0 : PQresultStatus(poResult) != PGRES_TUPLES_OK ||
539 0 : PQntuples(poResult) < 0)
540 : {
541 :
542 0 : if (poResult)
543 0 : PQclear(poResult);
544 :
545 0 : CPLError(CE_Failure, CPLE_AppDefined,
546 : "PostGISRasterRasterBand::IRasterIO(): %s",
547 0 : PQerrorMessage(poRDS->poConn));
548 :
549 : // Free the object that holds pointers to matching tiles
550 0 : CPLFree(papsMatchingTiles);
551 0 : return CE_Failure;
552 : }
553 :
554 : /**
555 : * No data. Return the buffer filled with nodata values
556 : **/
557 0 : else if (PQntuples(poResult) == 0)
558 : {
559 0 : PQclear(poResult);
560 :
561 : // Free the object that holds pointers to matching tiles
562 0 : CPLFree(papsMatchingTiles);
563 0 : return CE_None;
564 : }
565 :
566 : /**
567 : * Ok, we loop over the results
568 : **/
569 0 : int nTuples = PQntuples(poResult);
570 0 : for (i = 0; i < nTuples; i++)
571 : {
572 0 : const char *pszPKID = PQgetvalue(poResult, i, 0);
573 0 : const char *pszMetadata = PQgetvalue(poResult, i, 1);
574 0 : const char *pszRaster = PQgetvalue(poResult, i, 2);
575 0 : poRDS->CacheTile(pszMetadata, pszRaster, pszPKID, nBand,
576 : bAllBandCaching);
577 : } // All tiles have been added to cache
578 :
579 0 : PQclear(poResult);
580 : } // End missing tiles
581 :
582 : /* -------------------------------------------------------------------- */
583 : /* Overlay each source in turn over top this. */
584 : /* -------------------------------------------------------------------- */
585 :
586 0 : CPLErr eErr = CE_None;
587 : /* Sort tiles by ascending PKID, so that the draw order is deterministic. */
588 0 : if (poRDS->GetPrimaryKeyRef() != nullptr)
589 : {
590 0 : qsort(papsMatchingTiles, nFeatureCount,
591 : sizeof(PostGISRasterTileDataset *), SortTilesByPKID);
592 : }
593 :
594 0 : VRTSource::WorkingState oWorkingState;
595 0 : for (i = 0; i < nFeatureCount && eErr == CE_None; i++)
596 : {
597 0 : PostGISRasterTileDataset *poTile = papsMatchingTiles[i];
598 : PostGISRasterTileRasterBand *poTileBand =
599 0 : cpl::down_cast<PostGISRasterTileRasterBand *>(
600 : poTile->GetRasterBand(nBand));
601 0 : eErr = poTileBand->poSource->RasterIO(
602 : eDataType, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
603 : nBufYSize, eBufType, nPixelSpace, nLineSpace, nullptr,
604 0 : oWorkingState);
605 : }
606 :
607 : // Free the object that holds pointers to matching tiles
608 0 : CPLFree(papsMatchingTiles);
609 :
610 0 : return eErr;
611 : }
612 :
613 : /**
614 : * \brief Set the no data value for this band.
615 : * Parameters:
616 : * - double: The nodata value
617 : * Returns:
618 : * - CE_None.
619 : */
620 0 : CPLErr PostGISRasterRasterBand::SetNoDataValue(double dfNewValue)
621 : {
622 0 : m_dfNoDataValue = dfNewValue;
623 :
624 0 : return CE_None;
625 : }
626 :
627 : /**
628 : * \brief Fetch the no data value for this band.
629 : * Parameters:
630 : * - int *: pointer to a boolean to use to indicate if a value is actually
631 : * associated with this layer. May be NULL (default).
632 : * Returns:
633 : * - double: the nodata value for this band.
634 : */
635 0 : double PostGISRasterRasterBand::GetNoDataValue(int *pbSuccess)
636 : {
637 0 : if (pbSuccess != nullptr)
638 0 : *pbSuccess = m_bNoDataValueSet;
639 :
640 0 : return m_dfNoDataValue;
641 : }
642 :
643 : /***************************************************
644 : * \brief Return the number of overview layers available
645 : ***************************************************/
646 0 : int PostGISRasterRasterBand::GetOverviewCount()
647 : {
648 0 : PostGISRasterDataset *poRDS = cpl::down_cast<PostGISRasterDataset *>(poDS);
649 0 : return poRDS->GetOverviewCount();
650 : }
651 :
652 : /**********************************************************
653 : * \brief Fetch overview raster band object
654 : **********************************************************/
655 0 : GDALRasterBand *PostGISRasterRasterBand::GetOverview(int i)
656 : {
657 0 : if (i < 0 || i >= GetOverviewCount())
658 0 : return nullptr;
659 :
660 0 : PostGISRasterDataset *poRDS = cpl::down_cast<PostGISRasterDataset *>(poDS);
661 0 : PostGISRasterDataset *poOverviewDS = poRDS->GetOverviewDS(i);
662 0 : if (!poOverviewDS)
663 : {
664 0 : CPLAssert(false);
665 : return nullptr;
666 : }
667 0 : if (poOverviewDS->nBands == 0)
668 : {
669 0 : if (!poOverviewDS->SetRasterProperties(nullptr) ||
670 0 : poOverviewDS->GetRasterCount() != poRDS->GetRasterCount())
671 : {
672 0 : CPLDebug("PostGIS_Raster",
673 : "Request for overview %d of band %d failed", i, nBand);
674 0 : return nullptr;
675 : }
676 : }
677 :
678 0 : return poOverviewDS->GetRasterBand(nBand);
679 : }
680 :
681 : /**
682 : * \brief How should this band be interpreted as color?
683 : * GCI_Undefined is returned when the format doesn't know anything about the
684 : * color interpretation.
685 : **/
686 0 : GDALColorInterp PostGISRasterRasterBand::GetColorInterpretation()
687 : {
688 0 : if (poDS->GetRasterCount() == 1)
689 : {
690 0 : m_eColorInterp = GCI_GrayIndex;
691 : }
692 :
693 0 : else if (poDS->GetRasterCount() == 3)
694 : {
695 0 : if (nBand == 1)
696 0 : m_eColorInterp = GCI_RedBand;
697 0 : else if (nBand == 2)
698 0 : m_eColorInterp = GCI_GreenBand;
699 0 : else if (nBand == 3)
700 0 : m_eColorInterp = GCI_BlueBand;
701 : else
702 0 : m_eColorInterp = GCI_Undefined;
703 : }
704 :
705 : else
706 : {
707 0 : m_eColorInterp = GCI_Undefined;
708 : }
709 :
710 0 : return m_eColorInterp;
711 : }
712 :
713 : /************************************************************************/
714 : /* GetMinimum() */
715 : /************************************************************************/
716 :
717 0 : double PostGISRasterRasterBand::GetMinimum(int *pbSuccess)
718 : {
719 0 : PostGISRasterDataset *poRDS = cpl::down_cast<PostGISRasterDataset *>(poDS);
720 0 : if (poRDS->bBuildQuadTreeDynamically && poRDS->m_nTiles == 0)
721 : {
722 0 : if (pbSuccess)
723 0 : *pbSuccess = FALSE;
724 0 : return 0.0;
725 : }
726 0 : return VRTSourcedRasterBand::GetMaximum(pbSuccess);
727 : }
728 :
729 : /************************************************************************/
730 : /* GetMaximum() */
731 : /************************************************************************/
732 :
733 0 : double PostGISRasterRasterBand::GetMaximum(int *pbSuccess)
734 : {
735 0 : PostGISRasterDataset *poRDS = cpl::down_cast<PostGISRasterDataset *>(poDS);
736 0 : if (poRDS->bBuildQuadTreeDynamically && poRDS->m_nTiles == 0)
737 : {
738 0 : if (pbSuccess)
739 0 : *pbSuccess = FALSE;
740 0 : return 0.0;
741 : }
742 0 : return VRTSourcedRasterBand::GetMaximum(pbSuccess);
743 : }
744 :
745 : /************************************************************************/
746 : /* ComputeRasterMinMax() */
747 : /************************************************************************/
748 :
749 0 : CPLErr PostGISRasterRasterBand::ComputeRasterMinMax(int bApproxOK,
750 : double *adfMinMax)
751 : {
752 0 : if (nRasterXSize < 1024 && nRasterYSize < 1024)
753 0 : return VRTSourcedRasterBand::ComputeRasterMinMax(bApproxOK, adfMinMax);
754 :
755 0 : int nOverviewCount = GetOverviewCount();
756 0 : for (int i = 0; i < nOverviewCount; i++)
757 : {
758 0 : auto poOverview = GetOverview(i);
759 0 : if (poOverview->GetXSize() < 1024 && poOverview->GetYSize() < 1024)
760 0 : return poOverview->ComputeRasterMinMax(bApproxOK, adfMinMax);
761 : }
762 :
763 0 : return CE_Failure;
764 : }
|