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