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