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, Even Rouault
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's just necessary for overview band creation
42 */
PostGISRasterRasterBand(PostGISRasterDataset * poDS,int nBand,GDALDataType eDataType,GBool bNoDataValueSet,double dfNodata,GBool bIsOffline=false)43 PostGISRasterRasterBand::PostGISRasterRasterBand(
44 PostGISRasterDataset * poDS, int nBand,
45 GDALDataType eDataType, GBool bNoDataValueSet, double dfNodata,
46 GBool bIsOffline = false) :
47 VRTSourcedRasterBand(poDS, nBand)
48 {
49 /* Basic properties */
50 this->poDS = poDS;
51 this->bIsOffline = bIsOffline;
52 this->nBand = nBand;
53
54 this->eDataType = eDataType;
55 this->bNoDataValueSet = bNoDataValueSet;
56 this->dfNoDataValue = dfNodata;
57
58 this->pszSchema = poDS->pszSchema;
59 this->pszTable = poDS->pszTable;
60 this->pszColumn = poDS->pszColumn;
61
62 nRasterXSize = poDS->GetRasterXSize();
63 nRasterYSize = poDS->GetRasterYSize();
64
65 /*******************************************************************
66 * Finally, set the block size. We apply the same logic than in VRT
67 * driver.
68 *
69 * We limit the size of a block with MAX_BLOCK_SIZE here to prevent
70 * arrangements of just one big tile.
71 *
72 * This value is just used in case whe only have 1 tile in the
73 * table. Otherwise, the reading operations are performed by the
74 * sources, not the PostGISRasterBand object itself.
75 ******************************************************************/
76 this->nBlockXSize = MIN(MAX_BLOCK_SIZE, this->nRasterXSize);
77 this->nBlockYSize = MIN(MAX_BLOCK_SIZE, this->nRasterYSize);
78
79 #ifdef DEBUG_VERBOSE
80 CPLDebug("PostGIS_Raster",
81 "PostGISRasterRasterBand constructor: Band created (srid = %d)",
82 poDS->nSrid);
83
84 CPLDebug("PostGIS_Raster",
85 "PostGISRasterRasterBand constructor: Band size: (%d X %d)",
86 nRasterXSize, nRasterYSize);
87
88 CPLDebug("PostGIS_Raster", "PostGISRasterRasterBand::Constructor: "
89 "Block size (%dx%d)", this->nBlockXSize, this->nBlockYSize);
90 #endif
91 }
92
93 /***********************************************
94 * \brief: Band destructor
95 ***********************************************/
~PostGISRasterRasterBand()96 PostGISRasterRasterBand::~PostGISRasterRasterBand()
97 {
98 }
99
100 #ifdef notdef
101 /***********************************************************************
102 * \brief Set the block data to the null value if it is set, or zero if
103 * there is no null data value.
104 * Parameters:
105 * - void *: the block data
106 * Returns: nothing
107 **********************************************************************/
NullBlock(void * pData)108 void PostGISRasterRasterBand::NullBlock(void *pData)
109 {
110 int nWords = nBlockXSize * nBlockYSize;
111 int nDataTypeSize = GDALGetDataTypeSize(eDataType) / 8;
112
113 int bNoDataSet;
114 double dfNoData = GetNoDataValue(&bNoDataSet);
115 if (!bNoDataSet) {
116 memset(pData, 0, nWords * nDataTypeSize);
117 }
118
119 else {
120 GDALCopyWords(&dfNoData, GDT_Float64, 0,
121 pData, eDataType, nDataTypeSize,
122 nWords);
123 }
124 }
125
126 /***********************************************************************
127 * \brief Returns the metadata for this band
128 *
129 * If the metadata is actually stored in band's properties, simply
130 * returns them. Otherwise, it raises a query to fetch metadata
131 * FROM db
132 **********************************************************************/
GetBandMetadata(GDALDataType * peDataType,GBool * pbHasNoData,double * pdfNoData)133 GBool PostGISRasterRasterBand::GetBandMetadata(
134 GDALDataType * peDataType, GBool * pbHasNoData, double * pdfNoData)
135 {
136 // No need to raise a query
137 if (eDataType != GDT_Unknown) {
138 if (peDataType)
139 *peDataType = eDataType;
140
141 if (pbHasNoData)
142 *pbHasNoData = bNoDataValueSet;
143
144 if (pdfNoData)
145 *pdfNoData = dfNoDataValue;
146
147 return true;
148 }
149
150 /**
151 * Queries are expensive. So, we only raise them if all parameters
152 * are not null
153 **/
154 if (!peDataType || !pbHasNoData || !pdfNoData) {
155 return false;
156 }
157
158 /**
159 * It is safe to assume all the tiles will have the same values for
160 * metadata properties. That was checked during band's construction
161 * (or we simply trusted the user, to avoid expensive checkings).
162 * So, we can limit the results to just one.
163 **/
164 int nTuples = 0;
165 CPLString osCommand = NULL;
166 PGresult * poResult = NULL;
167 PostGISRasterDataset * poRDS = (PostGISRasterDataset *)poDS;
168
169 osCommand.Printf("st_bandpixeltype(%s, %d), "
170 "st_bandnodatavalue(%s, %d) is not null, "
171 "st_bandnodatavalue(%s, %d) FROM %s.%s limit 1", pszColumn,
172 nBand, pszColumn, nBand, pszColumn, nBand, pszSchema, pszTable);
173
174 #ifdef DEBUG_QUERY
175 CPLDebug("PostGIS_Raster",
176 "PostGISRasterRasterBand::GetBandMetadata(): Query: %s",
177 osCommand.c_str());
178 #endif
179
180 poResult = PQexec(poRDS->poConn, osCommand.c_str());
181 nTuples = PQntuples(poResult);
182
183 /* Error getting info FROM database */
184 if (poResult == NULL ||
185 PQresultStatus(poResult) != PGRES_TUPLES_OK ||
186 nTuples <= 0) {
187
188 ReportError(CE_Failure, CPLE_AppDefined,
189 "Error getting band metadata while creating raster "
190 "bands");
191
192 CPLDebug("PostGIS_Raster",
193 "PostGISRasterDataset::GetBandMetadata(): %s",
194 PQerrorMessage(poRDS->poConn));
195
196 if (poResult)
197 PQclear(poResult);
198
199 return false;
200 }
201
202 // Fill band metadata values
203 GBool bSignedByte = false;
204 int nBitsDepth = 8;
205 char* pszDataType = NULL;
206
207 pszDataType = CPLStrdup(PQgetvalue(poResult, 0, 0));
208
209 TranslateDataType(pszDataType, &eDataType, &nBitsDepth,
210 &bSignedByte);
211
212 // Add pixeltype to image structure domain
213 if (bSignedByte) {
214 SetMetadataItem("PIXELTYPE", "SIGNEDBYTE", "IMAGE_STRUCTURE" );
215 }
216
217 // Add NBITS to metadata only for sub-byte types
218 if (nBitsDepth < 8)
219 SetMetadataItem("NBITS", CPLString().Printf( "%d", nBitsDepth ),
220 "IMAGE_STRUCTURE" );
221
222 CPLFree(pszDataType);
223
224 bNoDataValueSet =
225 EQUALN(PQgetvalue(poResult, 0, 1), "t", sizeof(char));
226
227 dfNoDataValue = CPLAtof(PQgetvalue(poResult, 0, 2));
228
229 // Fill output arguments
230 *peDataType = eDataType;
231 *pbHasNoData = bNoDataValueSet;
232 *pdfNoData = dfNoDataValue;
233
234 return true;
235 }
236 #endif
237
238 /********************************************************
239 * \brief Set nodata value to a buffer
240 ********************************************************/
NullBuffer(void * pData,int nBufXSize,int nBufYSize,GDALDataType eBufType,int nPixelSpace,int nLineSpace)241 void PostGISRasterRasterBand::NullBuffer(void* pData,
242 int nBufXSize,
243 int nBufYSize,
244 GDALDataType eBufType,
245 int nPixelSpace,
246 int nLineSpace)
247 {
248 int j;
249 for(j = 0; j < nBufYSize; j++)
250 {
251 double dfVal = 0.0;
252 if( bNoDataValueSet )
253 dfVal = dfNoDataValue;
254 GDALCopyWords(&dfVal, GDT_Float64, 0,
255 (GByte*)pData + j * nLineSpace, eBufType, nPixelSpace,
256 nBufXSize);
257 }
258 }
259
260
261 /********************************************************
262 * \brief SortTilesByPKID
263 ********************************************************/
SortTilesByPKID(const void * a,const void * b)264 static int SortTilesByPKID(const void* a, const void* b)
265 {
266 PostGISRasterTileDataset* pa = *(PostGISRasterTileDataset** )a;
267 PostGISRasterTileDataset* pb = *(PostGISRasterTileDataset** )b;
268 return strcmp(pa->GetPKID(), pb->GetPKID());
269 }
270
271 /**
272 * Read/write a region of image data for this band.
273 *
274 * This method allows reading a region of a PostGISRasterBand into a buffer.
275 * The write support is still under development
276 *
277 * The function fetches all the raster data that intersects with the region
278 * provided, and store the data in the GDAL cache.
279 *
280 * It automatically takes care of data type translation if the data type
281 * (eBufType) of the buffer is different than that of the PostGISRasterRasterBand.
282 *
283 * The nPixelSpace and nLineSpace parameters allow reading into FROM various
284 * organization of buffers.
285 *
286 * @param eRWFlag Either GF_Read to read a region of data (GF_Write, to write
287 * a region of data, yet not supported)
288 *
289 * @param nXOff The pixel offset to the top left corner of the region of the
290 * band to be accessed. This would be zero to start FROM the left side.
291 *
292 * @param nYOff The line offset to the top left corner of the region of the band
293 * to be accessed. This would be zero to start FROM the top.
294 *
295 * @param nXSize The width of the region of the band to be accessed in pixels.
296 *
297 * @param nYSize The height of the region of the band to be accessed in lines.
298 *
299 * @param pData The buffer into which the data should be read, or FROM which it
300 * should be written. This buffer must contain at least
301 * nBufXSize * nBufYSize * nBandCount words of type eBufType. It is organized in
302 * left to right,top to bottom pixel order. Spacing is controlled by the
303 * nPixelSpace, and nLineSpace parameters.
304 *
305 * @param nBufXSize the width of the buffer image into which the desired region
306 * is to be read, or FROM which it is to be written.
307 *
308 * @param nBufYSize the height of the buffer image into which the desired region
309 * is to be read, or FROM which it is to be written.
310 *
311 * @param eBufType the type of the pixel values in the pData data buffer. The
312 * pixel values will automatically be translated to/FROM the
313 * PostGISRasterRasterBand data type as needed.
314 *
315 * @param nPixelSpace The byte offset FROM the start of one pixel value in pData
316 * to the start of the next pixel value within a scanline. If defaulted (0) the
317 * size of the datatype eBufType is used.
318 *
319 * @param nLineSpace The byte offset FROM the start of one scanline in pData to
320 * the start of the next. If defaulted (0) the size of the datatype
321 * eBufType * nBufXSize is used.
322 *
323 * @return CE_Failure if the access fails, otherwise CE_None.
324 */
325
IRasterIO(GDALRWFlag eRWFlag,int nXOff,int nYOff,int nXSize,int nYSize,void * pData,int nBufXSize,int nBufYSize,GDALDataType eBufType,GSpacing nPixelSpace,GSpacing nLineSpace,GDALRasterIOExtraArg * psExtraArg)326 CPLErr PostGISRasterRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
327 int nYOff, int nXSize, int nYSize, void * pData, int nBufXSize,
328 int nBufYSize, GDALDataType eBufType,
329 GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg* psExtraArg)
330 {
331 /**
332 * TODO: Write support not implemented yet
333 **/
334 if (eRWFlag == GF_Write) {
335 ReportError(CE_Failure, CPLE_NotSupported,
336 "Writing through PostGIS Raster band not supported yet");
337
338 return CE_Failure;
339 }
340
341 /*******************************************************************
342 * Do we have overviews that would be appropriate to satisfy this
343 * request?
344 ******************************************************************/
345 if( (nBufXSize < nXSize || nBufYSize < nYSize) &&
346 GetOverviewCount() > 0 )
347 {
348 if(OverviewRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
349 pData, nBufXSize, nBufYSize, eBufType, nPixelSpace,
350 nLineSpace, psExtraArg) == CE_None)
351
352 return CE_None;
353 }
354
355 PostGISRasterDataset * poRDS = (PostGISRasterDataset *)poDS;
356
357 int bSameWindowAsOtherBand =
358 (nXOff == poRDS->nXOffPrev &&
359 nYOff == poRDS->nYOffPrev &&
360 nXSize == poRDS->nXSizePrev &&
361 nYSize == poRDS->nYSizePrev);
362 poRDS->nXOffPrev = nXOff;
363 poRDS->nYOffPrev = nYOff;
364 poRDS->nXSizePrev = nXSize;
365 poRDS->nYSizePrev = nYSize;
366
367 /* Logic to determine if bands are read in order 1, 2, ... N */
368 /* If so, then use multi-band caching, otherwise do just single band caching */
369 if( poRDS->bAssumeMultiBandReadPattern )
370 {
371 if( nBand != poRDS->nNextExpectedBand )
372 {
373 CPLDebug("PostGIS_Raster",
374 "Disabling multi-band caching since band access pattern does not match");
375 poRDS->bAssumeMultiBandReadPattern = false;
376 poRDS->nNextExpectedBand = 1;
377 }
378 else
379 {
380 poRDS->nNextExpectedBand ++;
381 if( poRDS->nNextExpectedBand > poRDS->GetRasterCount() )
382 poRDS->nNextExpectedBand = 1;
383 }
384 }
385 else
386 {
387 if( nBand == poRDS->nNextExpectedBand )
388 {
389 poRDS->nNextExpectedBand ++;
390 if( poRDS->nNextExpectedBand > poRDS->GetRasterCount() )
391 {
392 CPLDebug("PostGIS_Raster", "Re-enabling multi-band caching");
393 poRDS->bAssumeMultiBandReadPattern = true;
394 poRDS->nNextExpectedBand = 1;
395 }
396 }
397 }
398
399 #ifdef DEBUG_VERBOSE
400 CPLDebug("PostGIS_Raster",
401 "PostGISRasterRasterBand::IRasterIO: "
402 "nBand = %d, nXOff = %d, nYOff = %d, nXSize = %d, nYSize = %d, nBufXSize = %d, nBufYSize = %d",
403 nBand, nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize);
404 #endif
405
406 #ifdef notdef
407 /*******************************************************************
408 * Optimization: We just have one tile. So, we can read it with
409 * IReadBlock
410 *
411 * TODO: Review it. It's not working (see comment in
412 * PostGISRasterDataset::ConstructOneDatasetFromTiles)
413 ******************************************************************/
414
415 if (poRDS->nTiles == 1) {
416
417 return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize,
418 nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace,
419 nLineSpace, psExtraArg);
420 }
421 #endif
422
423 /*******************************************************************
424 * Several tiles: we first look in all our sources caches. Missing
425 * blocks are queried
426 ******************************************************************/
427 double adfProjWin[8];
428 int nFeatureCount = 0;
429 CPLRectObj sAoi;
430
431 poRDS->PolygonFromCoords(nXOff, nYOff, nXOff + nXSize, nYOff + nYSize, adfProjWin);
432 // (p[6], p[7]) is the minimum (x, y), and (p[2], p[3]) the max
433 sAoi.minx = adfProjWin[6];
434 sAoi.maxx = adfProjWin[2];
435 if( adfProjWin[7] < adfProjWin[3] )
436 {
437 sAoi.miny = adfProjWin[7];
438 sAoi.maxy = adfProjWin[3];
439 }
440 else
441 {
442 sAoi.maxy = adfProjWin[7];
443 sAoi.miny = adfProjWin[3];
444 }
445
446 #ifdef DEBUG_VERBOSE
447 CPLDebug("PostGIS_Raster",
448 "PostGISRasterRasterBand::IRasterIO: "
449 "Intersection box: (%f, %f) - (%f, %f)", sAoi.minx,
450 sAoi.miny, sAoi.maxx, sAoi.maxy);
451 #endif
452
453 if (poRDS->hQuadTree == NULL)
454 {
455 ReportError(CE_Failure, CPLE_AppDefined,
456 "Could not read metadata index.");
457 return CE_Failure;
458 }
459
460 NullBuffer(pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace);
461
462 if( poRDS->bBuildQuadTreeDynamically && !bSameWindowAsOtherBand )
463 {
464 if( !(poRDS->LoadSources(nXOff, nYOff, nXSize, nYSize, nBand)) )
465 return CE_Failure;
466 }
467
468 // Matching sources, to avoid a dumb for loop over the sources
469 PostGISRasterTileDataset ** papsMatchingTiles =
470 (PostGISRasterTileDataset **)
471 CPLQuadTreeSearch(poRDS->hQuadTree, &sAoi, &nFeatureCount);
472
473 // No blocks found. This is not an error (the raster may have holes)
474 if (nFeatureCount == 0) {
475 CPLFree(papsMatchingTiles);
476
477 return CE_None;
478 }
479
480 int i;
481
482 /**
483 * We need to store the max, min coords for the missing tiles in
484 * any place. This is as good as any other
485 **/
486 sAoi.minx = 0.0;
487 sAoi.miny = 0.0;
488 sAoi.maxx = 0.0;
489 sAoi.maxy = 0.0;
490
491 GIntBig nMemoryRequiredForTiles = 0;
492 CPLString osIDsToFetch;
493 int nTilesToFetch = 0;
494 int nBandDataTypeSize = GDALGetDataTypeSize(eDataType) / 8;
495
496 // Loop just over the intersecting sources
497 for(i = 0; i < nFeatureCount; i++) {
498 PostGISRasterTileDataset *poTile = papsMatchingTiles[i];
499 PostGISRasterTileRasterBand* poTileBand =
500 (PostGISRasterTileRasterBand *)poTile->GetRasterBand(nBand);
501
502 nMemoryRequiredForTiles += poTileBand->GetXSize() * poTileBand->GetYSize() *
503 nBandDataTypeSize;
504
505 // Missing tile: we'll need to query for it
506 if (!poTileBand->IsCached()) {
507
508 // If we have a PKID, add the tile PKID to the list
509 if (poTile->pszPKID != NULL)
510 {
511 if( osIDsToFetch.size() != 0 )
512 osIDsToFetch += ",";
513 osIDsToFetch += "'";
514 osIDsToFetch += poTile->pszPKID;
515 osIDsToFetch += "'";
516 }
517
518 double dfTileMinX, dfTileMinY, dfTileMaxX, dfTileMaxY;
519 poTile->GetExtent(&dfTileMinX, &dfTileMinY,
520 &dfTileMaxX, &dfTileMaxY);
521
522 /**
523 * We keep the general max and min values of all the missing
524 * tiles, to raise a query that intersect just that area.
525 *
526 * TODO: In case of just a few tiles and very separated,
527 * this strategy is clearly suboptimal. We'll get our
528 * missing tiles, but with a lot of other not needed tiles.
529 *
530 * A possible optimization will be to simply rely on the
531 * I/O method of the source (must be implemented), in case
532 * we have minus than a reasonable amount of tiles missing.
533 * Another criteria to decide would be how separated the
534 * tiles are. Two queries for just two adjacent tiles is
535 * also a dumb strategy.
536 **/
537 if( nTilesToFetch == 0 )
538 {
539 sAoi.minx = dfTileMinX;
540 sAoi.miny = dfTileMinY;
541 sAoi.maxx = dfTileMaxX;
542 sAoi.maxy = dfTileMaxY;
543 }
544 else
545 {
546 if (dfTileMinX < sAoi.minx)
547 sAoi.minx = dfTileMinX;
548
549 if (dfTileMinY < sAoi.miny)
550 sAoi.miny = dfTileMinY;
551
552 if (dfTileMaxX > sAoi.maxx)
553 sAoi.maxx = dfTileMaxX;
554
555 if (dfTileMaxY > sAoi.maxy)
556 sAoi.maxy = dfTileMaxY;
557 }
558
559 nTilesToFetch ++;
560
561 }
562 }
563
564 /* Determine caching strategy */
565 int bAllBandCaching = FALSE;
566 if (nTilesToFetch > 0)
567 {
568 GIntBig nCacheMax = (GIntBig) GDALGetCacheMax64();
569 if( nMemoryRequiredForTiles > nCacheMax )
570 {
571 CPLDebug("PostGIS_Raster",
572 "For best performance, the block cache should be able to store " CPL_FRMT_GIB
573 " bytes for the tiles of the requested window, "
574 "but it is only " CPL_FRMT_GIB " byte large",
575 nMemoryRequiredForTiles, nCacheMax );
576 nTilesToFetch = 0;
577 }
578
579 if( poRDS->GetRasterCount() > 1 && poRDS->bAssumeMultiBandReadPattern )
580 {
581 GIntBig nMemoryRequiredForTilesAllBands =
582 nMemoryRequiredForTiles * poRDS->GetRasterCount();
583 if( nMemoryRequiredForTilesAllBands <= nCacheMax )
584 {
585 bAllBandCaching = TRUE;
586 }
587 else
588 {
589 CPLDebug("PostGIS_Raster", "Caching only this band, but not all bands. "
590 "Cache should be " CPL_FRMT_GIB " byte large for that",
591 nMemoryRequiredForTilesAllBands);
592 }
593 }
594 }
595
596 // Raise a query for missing tiles and cache them
597 if (nTilesToFetch > 0) {
598
599 /**
600 * There are several options here, to raise the query.
601 * - Get all the tiles which PKID is in a list of missing
602 * PKIDs.
603 * - Get all the tiles that intersect a polygon constructed
604 * based on the (min - max) values calculated before.
605 * - Get all the tiles with upper left pixel included in the
606 * range (min - max) calculated before.
607 *
608 * The first option is the most efficient one when a PKID exists.
609 * After that, the second one is the most efficient one when a
610 * spatial index exists.
611 * The third one is the only one available when neither a PKID or spatial
612 * index exist.
613 **/
614 CPLString osCommand;
615 PGresult * poResult;
616
617 CPLString osRasterToFetch;
618 if (bAllBandCaching)
619 osRasterToFetch = pszColumn;
620 else
621 osRasterToFetch.Printf("ST_Band(%s, %d)", pszColumn, nBand);
622
623 int bHasWhere = FALSE;
624 if (osIDsToFetch.size() && (poRDS->bIsFastPK || !(poRDS->HasSpatialIndex())) ) {
625 osCommand.Printf("SELECT %s, "
626 "ST_Metadata(%s), %s FROM %s.%s",
627 osRasterToFetch.c_str(), pszColumn,
628 poRDS->GetPrimaryKeyRef(), pszSchema, pszTable);
629 if( nTilesToFetch < poRDS->nTiles || poRDS->bBuildQuadTreeDynamically )
630 {
631 bHasWhere = TRUE;
632 osCommand += " WHERE ";
633 osCommand += poRDS->pszPrimaryKeyName;
634 osCommand += " IN (";
635 osCommand += osIDsToFetch;
636 osCommand += ")";
637 }
638 }
639
640 else {
641 bHasWhere = TRUE;
642 osCommand.Printf("SELECT %s, ST_Metadata(%s), %s FROM %s.%s WHERE ",
643 osRasterToFetch.c_str(), pszColumn,
644 (poRDS->GetPrimaryKeyRef()) ? poRDS->GetPrimaryKeyRef() : "'foo'",
645 pszSchema, pszTable);
646 if( poRDS->HasSpatialIndex() )
647 {
648 osCommand += CPLSPrintf("%s && "
649 "ST_GeomFromText('POLYGON((%.18f %.18f,%.18f %.18f,%.18f %.18f,%.18f %.18f,%.18f %.18f))')",
650 pszColumn,
651 adfProjWin[0], adfProjWin[1],
652 adfProjWin[2], adfProjWin[3],
653 adfProjWin[4], adfProjWin[5],
654 adfProjWin[6], adfProjWin[7],
655 adfProjWin[0], adfProjWin[1]);
656 }
657 else
658 {
659 #define EPS 1e-5
660 osCommand += CPLSPrintf("ST_UpperLeftX(%s)"
661 " BETWEEN %f AND %f AND ST_UpperLeftY(%s) BETWEEN "
662 "%f AND %f", pszColumn, sAoi.minx-EPS, sAoi.maxx+EPS,
663 pszColumn, sAoi.miny-EPS, sAoi.maxy+EPS);
664 }
665 }
666
667 if( poRDS->pszWhere != NULL )
668 {
669 if( bHasWhere )
670 osCommand += " AND (";
671 else
672 osCommand += " WHERE (";
673 osCommand += poRDS->pszWhere;
674 osCommand += ")";
675 }
676
677 poResult = PQexec(poRDS->poConn, osCommand.c_str());
678
679 #ifdef DEBUG_QUERY
680 CPLDebug("PostGIS_Raster",
681 "PostGISRasterRasterBand::IRasterIO(): Query = \"%s\" --> number of rows = %d",
682 osCommand.c_str(), poResult ? PQntuples(poResult) : 0 );
683 #endif
684
685 if (poResult == NULL ||
686 PQresultStatus(poResult) != PGRES_TUPLES_OK ||
687 PQntuples(poResult) < 0) {
688
689 if (poResult)
690 PQclear(poResult);
691
692 CPLError(CE_Failure, CPLE_AppDefined,
693 "PostGISRasterRasterBand::IRasterIO(): %s",
694 PQerrorMessage(poRDS->poConn));
695
696 // Free the object that holds pointers to matching tiles
697 CPLFree(papsMatchingTiles);
698 return CE_Failure;
699 }
700
701 /**
702 * No data. Return the buffer filled with nodata values
703 **/
704 else if (PQntuples(poResult) == 0) {
705 PQclear(poResult);
706
707 // Free the object that holds pointers to matching tiles
708 CPLFree(papsMatchingTiles);
709 return CE_None;
710 }
711
712 /**
713 * Ok, we loop over the results
714 **/
715 int nTuples = PQntuples(poResult);
716 for(i = 0; i < nTuples; i++)
717 {
718 const char* pszMetadata = PQgetvalue(poResult, i, 1);
719 const char* pszRaster = PQgetvalue(poResult, i, 0);
720 const char *pszPKID = (poRDS->GetPrimaryKeyRef() != NULL) ? PQgetvalue(poResult, i, 2) : NULL;
721 poRDS->CacheTile(pszMetadata, pszRaster, pszPKID, nBand, bAllBandCaching);
722 } // All tiles have been added to cache
723
724 PQclear(poResult);
725 } // End missing tiles
726
727 /* -------------------------------------------------------------------- */
728 /* Overlay each source in turn over top this. */
729 /* -------------------------------------------------------------------- */
730
731 CPLErr eErr = CE_None;
732 /* Sort tiles by ascending PKID, so that the draw order is determinist */
733 if( poRDS->GetPrimaryKeyRef() != NULL )
734 {
735 qsort(papsMatchingTiles, nFeatureCount, sizeof(PostGISRasterTileDataset*),
736 SortTilesByPKID);
737 }
738
739 for(i = 0; i < nFeatureCount && eErr == CE_None; i++)
740 {
741 PostGISRasterTileDataset *poTile = papsMatchingTiles[i];
742 PostGISRasterTileRasterBand* poTileBand =
743 (PostGISRasterTileRasterBand *)poTile->GetRasterBand(nBand);
744 eErr =
745 poTileBand->poSource->RasterIO( nXOff, nYOff, nXSize, nYSize,
746 pData, nBufXSize, nBufYSize,
747 eBufType, nPixelSpace, nLineSpace, NULL);
748 }
749
750 // Free the object that holds pointers to matching tiles
751 CPLFree(papsMatchingTiles);
752
753 return eErr;
754 }
755
756 /**
757 * \brief Set the no data value for this band.
758 * Parameters:
759 * - double: The nodata value
760 * Returns:
761 * - CE_None.
762 */
SetNoDataValue(double dfNewValue)763 CPLErr PostGISRasterRasterBand::SetNoDataValue(double dfNewValue) {
764 dfNoDataValue = dfNewValue;
765
766 return CE_None;
767 }
768
769 /**
770 * \brief Fetch the no data value for this band.
771 * Parameters:
772 * - int *: pointer to a boolean to use to indicate if a value is actually
773 * associated with this layer. May be NULL (default).
774 * Returns:
775 * - double: the nodata value for this band.
776 */
GetNoDataValue(int * pbSuccess)777 double PostGISRasterRasterBand::GetNoDataValue(int *pbSuccess) {
778 if (pbSuccess != NULL)
779 *pbSuccess = (int) bNoDataValueSet;
780
781 return dfNoDataValue;
782 }
783
784 /***************************************************
785 * \brief Return the number of overview layers available
786 ***************************************************/
GetOverviewCount()787 int PostGISRasterRasterBand::GetOverviewCount()
788 {
789 PostGISRasterDataset * poRDS = (PostGISRasterDataset *)poDS;
790 return poRDS->GetOverviewCount();
791 }
792
793 /**********************************************************
794 * \brief Fetch overview raster band object
795 **********************************************************/
GetOverview(int i)796 GDALRasterBand * PostGISRasterRasterBand::GetOverview(int i)
797 {
798 if (i < 0 || i >= GetOverviewCount())
799 return NULL;
800
801 PostGISRasterDataset* poRDS = (PostGISRasterDataset *)poDS;
802 PostGISRasterDataset* poOverviewDS = poRDS->GetOverviewDS(i);
803 if( poOverviewDS->nBands == 0 )
804 {
805 if (!poOverviewDS->SetRasterProperties(NULL) ||
806 poOverviewDS->GetRasterCount() != poRDS->GetRasterCount())
807 {
808 CPLDebug("PostGIS_Raster",
809 "Request for overview %d of band %d failed", i, nBand);
810 return NULL;
811 }
812 }
813
814 return poOverviewDS->GetRasterBand(nBand);
815 }
816
817 #ifdef notdef
818 /*****************************************************
819 * \brief Read a natural block of raster band data
820 *****************************************************/
IReadBlock(int nBlockXOff,int nBlockYOff,void * pImage)821 CPLErr PostGISRasterRasterBand::IReadBlock(int nBlockXOff,
822 int nBlockYOff, void * pImage)
823 {
824 PGresult * poResult = NULL;
825 CPLString osCommand;
826 int nXOff = 0;
827 int nYOff = 0;
828 int nNaturalBlockXSize = 0;
829 int nNaturalBlockYSize = 0;
830 double adfProjWin[8];
831 PostGISRasterDataset * poRDS = (PostGISRasterDataset *)poDS;
832
833 int nPixelSize = GDALGetDataTypeSize(eDataType) / 8;
834
835 // Construct a polygon to intersect with
836 GetBlockSize(&nNaturalBlockXSize, &nNaturalBlockYSize);
837
838 nXOff = nBlockXOff * nNaturalBlockXSize;
839 nYOff = nBlockYOff * nNaturalBlockYSize;
840
841 poRDS->PolygonFromCoords(nXOff, nYOff, nXOff + nNaturalBlockXSize, nYOff + nNaturalBlockYSize, adfProjWin);
842
843 // Raise the query
844 if (poRDS->pszWhere == NULL) {
845 osCommand.Printf("SELECT st_band(%s, %d) FROM %s.%s "
846 "WHERE st_intersects(%s, ST_PolygonFromText"
847 "('POLYGON((%.17f %.17f, %.17f %.17f, %.17f %.17f, %.17f "
848 "%.17f, %.17f %.17f))', %d))", pszColumn, nBand, pszSchema,
849 pszTable, pszColumn, adfProjWin[0], adfProjWin[1],
850 adfProjWin[2], adfProjWin[3], adfProjWin[4], adfProjWin[5],
851 adfProjWin[6], adfProjWin[7], adfProjWin[0], adfProjWin[1],
852 poRDS->nSrid);
853 }
854
855 else {
856 osCommand.Printf("SELECT st_band(%s, %d) FROM %s.%s WHERE (%s) "
857 "AND st_intersects(%s, ST_PolygonFromText"
858 "('POLYGON((%.17f %.17f, %.17f %.17f, %.17f %.17f, %.17f "
859 "%.17f, %.17f %.17f))', %d))", pszColumn, nBand, pszSchema,
860 pszTable, poRDS->pszWhere, pszColumn, adfProjWin[0],
861 adfProjWin[1], adfProjWin[2], adfProjWin[3], adfProjWin[4],
862 adfProjWin[5], adfProjWin[6], adfProjWin[7], adfProjWin[0],
863 adfProjWin[1], poRDS->nSrid);
864 }
865
866 #ifdef DEBUG_QUERY
867 CPLDebug("PostGIS_Raster",
868 "PostGISRasterRasterBand::IReadBlock(): Query = %s",
869 osCommand.c_str());
870 #endif
871
872 poResult = PQexec(poRDS->poConn, osCommand.c_str());
873 if (poResult == NULL ||
874 PQresultStatus(poResult) != PGRES_TUPLES_OK ||
875 PQntuples(poResult) < 0) {
876
877 if (poResult)
878 PQclear(poResult);
879
880 ReportError(CE_Failure, CPLE_AppDefined,
881 "Error retrieving raster data FROM database");
882
883 CPLDebug("PostGIS_Raster",
884 "PostGISRasterRasterBand::IRasterIO(): %s",
885 PQerrorMessage(poRDS->poConn));
886
887 return CE_Failure;
888 }
889
890 /**
891 * No data. Return the buffer filled with nodata values
892 **/
893 else if (PQntuples(poResult) == 0) {
894 PQclear(poResult);
895
896 CPLDebug("PostGIS_Raster",
897 "PostGISRasterRasterBand::IRasterIO(): Null block");
898
899 NullBlock(pImage);
900
901 return CE_None;
902 }
903
904
905 /**
906 * Ok, we get the data. Only data size, without payload
907 *
908 * TODO: Check byte order
909 **/
910 int nExpectedDataSize = nNaturalBlockXSize * nNaturalBlockYSize *
911 nPixelSize;
912
913 int nWKBLength = 0;
914
915 GByte * pbyData = CPLHexToBinary(PQgetvalue(poResult, 0, 0),
916 &nWKBLength);
917
918 char * pbyDataToRead = (char*)GET_BAND_DATA(pbyData,nBand,
919 nPixelSize, nExpectedDataSize);
920
921 memcpy(pImage, pbyDataToRead, nExpectedDataSize * sizeof(char));
922
923 CPLDebug("PostGIS_Raster", "IReadBlock: Copied %d bytes FROM block "
924 "(%d, %d) to %p", nExpectedDataSize, nBlockXOff,
925 nBlockYOff, pImage);
926
927 CPLFree(pbyData);
928 PQclear(poResult);
929
930 return CE_None;
931 }
932 #endif
933
934 /**
935 * \brief How should this band be interpreted as color?
936 * GCI_Undefined is returned when the format doesn't know anything about the
937 * color interpretation.
938 **/
GetColorInterpretation()939 GDALColorInterp PostGISRasterRasterBand::GetColorInterpretation()
940 {
941 if (poDS->GetRasterCount() == 1) {
942 eColorInterp = GCI_GrayIndex;
943 }
944
945 else if (poDS->GetRasterCount() == 3) {
946 if (nBand == 1)
947 eColorInterp = GCI_RedBand;
948 else if( nBand == 2 )
949 eColorInterp = GCI_GreenBand;
950 else if( nBand == 3 )
951 eColorInterp = GCI_BlueBand;
952 else
953 eColorInterp = GCI_Undefined;
954 }
955
956 else {
957 eColorInterp = GCI_Undefined;
958 }
959
960 return eColorInterp;
961 }
962
963 /************************************************************************/
964 /* GetMinimum() */
965 /************************************************************************/
966
GetMinimum(int * pbSuccess)967 double PostGISRasterRasterBand::GetMinimum( int *pbSuccess )
968 {
969 PostGISRasterDataset * poRDS = (PostGISRasterDataset *)poDS;
970 if( poRDS->bBuildQuadTreeDynamically && poRDS->nTiles == 0 )
971 {
972 if( pbSuccess )
973 *pbSuccess = FALSE;
974 return 0.0;
975 }
976 return VRTSourcedRasterBand::GetMaximum(pbSuccess);
977 }
978
979 /************************************************************************/
980 /* GetMaximum() */
981 /************************************************************************/
982
GetMaximum(int * pbSuccess)983 double PostGISRasterRasterBand::GetMaximum( int *pbSuccess )
984 {
985 PostGISRasterDataset * poRDS = (PostGISRasterDataset *)poDS;
986 if( poRDS->bBuildQuadTreeDynamically && poRDS->nTiles == 0 )
987 {
988 if( pbSuccess )
989 *pbSuccess = FALSE;
990 return 0.0;
991 }
992 return VRTSourcedRasterBand::GetMaximum(pbSuccess);
993 }
994
995 /************************************************************************/
996 /* ComputeRasterMinMax() */
997 /************************************************************************/
998
ComputeRasterMinMax(int bApproxOK,double * adfMinMax)999 CPLErr PostGISRasterRasterBand::ComputeRasterMinMax( int bApproxOK, double* adfMinMax )
1000 {
1001 if( nRasterXSize < 1024 && nRasterYSize < 1024 )
1002 return VRTSourcedRasterBand::ComputeRasterMinMax(bApproxOK, adfMinMax);
1003
1004 int nOverviewCount = GetOverviewCount();
1005 for(int i = 0; i < nOverviewCount; i++)
1006 {
1007 if( GetOverview(i)->GetXSize() < 1024 && GetOverview(i)->GetYSize() < 1024 )
1008 return GetOverview(i)->ComputeRasterMinMax(bApproxOK, adfMinMax);
1009 }
1010
1011 return CE_Failure;
1012 }
1013