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