Line data Source code
1 : /***********************************************************************
2 : * File : postgisrastertilerasterband.cpp
3 : * Project: PostGIS Raster driver
4 : * Purpose: GDAL Tile RasterBand implementation for PostGIS Raster
5 : * driver
6 : * Author: Jorge Arevalo, jorge.arevalo@deimos-space.com
7 : * jorgearevalo@libregis.org
8 : * Last changes: $Id$
9 : *
10 : ***********************************************************************
11 : * Copyright (c) 2009 - 2013, Jorge Arevalo
12 : * Copyright (c) 2013-2018, Even Rouault
13 : *
14 : * SPDX-License-Identifier: MIT
15 : **********************************************************************/
16 : #include "postgisraster.h"
17 : #include <memory>
18 :
19 : /************************
20 : * \brief Constructor
21 : ************************/
22 0 : PostGISRasterTileRasterBand::PostGISRasterTileRasterBand(
23 0 : PostGISRasterTileDataset *poRTDSIn, int nBandIn, GDALDataType eDataTypeIn)
24 0 : : poSource(nullptr)
25 : {
26 : // Basic properties.
27 0 : poDS = poRTDSIn;
28 0 : nBand = nBandIn;
29 :
30 : #if 0
31 : CPLDebug("PostGIS_Raster",
32 : "PostGISRasterTileRasterBand::Constructor: Raster tile dataset "
33 : "of dimensions %dx%d", poRTDS->GetRasterXSize(),
34 : poRTDS->GetRasterYSize());
35 : #endif
36 :
37 0 : eDataType = eDataTypeIn;
38 :
39 0 : nRasterXSize = poRTDSIn->GetRasterXSize();
40 0 : nRasterYSize = poRTDSIn->GetRasterYSize();
41 :
42 0 : nBlockXSize = nRasterXSize;
43 0 : nBlockYSize = nRasterYSize;
44 0 : }
45 :
46 : /************************
47 : * \brief Destructor
48 : ************************/
49 0 : PostGISRasterTileRasterBand::~PostGISRasterTileRasterBand()
50 : {
51 0 : }
52 :
53 : /***********************************************************************
54 : * \brief Returns true if the (only) block is stored in the cache
55 : **********************************************************************/
56 0 : GBool PostGISRasterTileRasterBand::IsCached()
57 : {
58 0 : GDALRasterBlock *poBlock = TryGetLockedBlockRef(0, 0);
59 0 : if (poBlock != nullptr)
60 : {
61 0 : poBlock->DropLock();
62 0 : return true;
63 : }
64 :
65 0 : return false;
66 : }
67 :
68 : /*****************************************************
69 : * \brief Read a natural block of raster band data
70 : *****************************************************/
71 0 : CPLErr PostGISRasterTileRasterBand::IReadBlock(int /*nBlockXOff*/,
72 : int /*nBlockYOff*/, void *pImage)
73 : {
74 0 : CPLString osCommand;
75 0 : PGresult *poResult = nullptr;
76 0 : int nWKBLength = 0;
77 :
78 0 : const int nPixelSize = GDALGetDataTypeSizeBytes(eDataType);
79 :
80 : PostGISRasterTileDataset *poRTDS =
81 0 : cpl::down_cast<PostGISRasterTileDataset *>(poDS);
82 :
83 0 : const double dfTileUpperLeftX =
84 : poRTDS->adfGeoTransform[GEOTRSFRM_TOPLEFT_X];
85 0 : const double dfTileUpperLeftY =
86 : poRTDS->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y];
87 0 : const double dfTileResX = poRTDS->adfGeoTransform[1];
88 0 : const double dfTileResY = poRTDS->adfGeoTransform[5];
89 0 : const int nTileXSize = nBlockXSize;
90 0 : const int nTileYSize = nBlockYSize;
91 :
92 0 : CPLString osSchemaI(CPLQuotedSQLIdentifier(poRTDS->poRDS->pszSchema));
93 0 : CPLString osTableI(CPLQuotedSQLIdentifier(poRTDS->poRDS->pszTable));
94 0 : CPLString osColumnI(CPLQuotedSQLIdentifier(poRTDS->poRDS->pszColumn));
95 :
96 0 : CPLString osRasterToFetch;
97 0 : osRasterToFetch.Printf("ST_Band(%s, %d)", osColumnI.c_str(), nBand);
98 : // We don't honour CLIENT_SIDE_IF_POSSIBLE since it would be likely too
99 : // costly in that context.
100 0 : if (poRTDS->poRDS->eOutDBResolution != OutDBResolution::CLIENT_SIDE)
101 : {
102 : osRasterToFetch =
103 0 : "encode(ST_AsBinary(" + osRasterToFetch + ",TRUE),'hex')";
104 : }
105 :
106 : osCommand.Printf("SELECT %s FROM %s.%s WHERE ", osRasterToFetch.c_str(),
107 0 : osSchemaI.c_str(), osTableI.c_str());
108 :
109 : // Get by PKID
110 0 : if (poRTDS->poRDS->pszPrimaryKeyName)
111 : {
112 : CPLString osPrimaryKeyNameI(
113 0 : CPLQuotedSQLIdentifier(poRTDS->poRDS->pszPrimaryKeyName));
114 : osCommand +=
115 0 : CPLSPrintf("%s = '%s'", osPrimaryKeyNameI.c_str(), poRTDS->pszPKID);
116 : }
117 :
118 : // Get by upperleft
119 : else
120 : {
121 : osCommand += CPLSPrintf("abs(ST_UpperLeftX(%s) - %.8f) < 1e-8 and "
122 : "abs(ST_UpperLeftY(%s) - %.8f) < 1e-8",
123 : osColumnI.c_str(), dfTileUpperLeftX,
124 0 : osColumnI.c_str(), dfTileUpperLeftY);
125 : }
126 :
127 0 : poResult = PQexec(poRTDS->poRDS->poConn, osCommand.c_str());
128 :
129 : #ifdef DEBUG_QUERY
130 : CPLDebug("PostGIS_Raster",
131 : "PostGISRasterTileRasterBand::IReadBlock(): "
132 : "Query = \"%s\" --> number of rows = %d",
133 : osCommand.c_str(), poResult ? PQntuples(poResult) : 0);
134 : #endif
135 :
136 0 : if (poResult == nullptr || PQresultStatus(poResult) != PGRES_TUPLES_OK ||
137 0 : PQntuples(poResult) <= 0)
138 : {
139 :
140 0 : CPLString osError;
141 0 : if (PQresultStatus(poResult) == PGRES_FATAL_ERROR)
142 : {
143 0 : const char *pszError = PQerrorMessage(poRTDS->poRDS->poConn);
144 0 : if (pszError)
145 0 : osError = pszError;
146 : }
147 0 : if (poResult)
148 0 : PQclear(poResult);
149 :
150 0 : ReportError(CE_Failure, CPLE_AppDefined,
151 : "Error getting block of data (upperpixel = %f, %f): %s",
152 : dfTileUpperLeftX, dfTileUpperLeftY, osError.c_str());
153 :
154 0 : return CE_Failure;
155 : }
156 :
157 : /* Copy only data size, without payload */
158 0 : int nExpectedDataSize = nBlockXSize * nBlockYSize * nPixelSize;
159 :
160 : struct CPLFreer
161 : {
162 0 : void operator()(GByte *x) const
163 : {
164 0 : CPLFree(x);
165 0 : }
166 : };
167 :
168 : std::unique_ptr<GByte, CPLFreer> pbyDataAutoFreed(
169 0 : CPLHexToBinary(PQgetvalue(poResult, 0, 0), &nWKBLength));
170 0 : GByte *pbyData = pbyDataAutoFreed.get();
171 0 : PQclear(poResult);
172 :
173 0 : const int nMinimumWKBLength = RASTER_HEADER_SIZE + BAND_SIZE(1, nPixelSize);
174 0 : if (nWKBLength < nMinimumWKBLength)
175 : {
176 0 : CPLDebug("PostGIS_Raster",
177 : "nWKBLength=%d. too short. Expected at least %d", nWKBLength,
178 : nMinimumWKBLength);
179 0 : return CE_Failure;
180 : }
181 :
182 : // Is it indb-raster ?
183 0 : if ((pbyData[RASTER_HEADER_SIZE] & 0x80) == 0)
184 : {
185 0 : int nExpectedWKBLength =
186 0 : RASTER_HEADER_SIZE + BAND_SIZE(nPixelSize, nExpectedDataSize);
187 0 : if (nWKBLength != nExpectedWKBLength)
188 : {
189 0 : CPLDebug("PostGIS_Raster", "nWKBLength=%d, nExpectedWKBLength=%d",
190 : nWKBLength, nExpectedWKBLength);
191 0 : return CE_Failure;
192 : }
193 :
194 0 : GByte *pbyDataToRead =
195 0 : GET_BAND_DATA(pbyData, 1, nPixelSize, nExpectedDataSize);
196 :
197 : // Do byte-swapping if necessary */
198 0 : const bool bIsLittleEndian = (pbyData[0] == 1);
199 : #ifdef CPL_LSB
200 0 : const bool bSwap = !bIsLittleEndian;
201 : #else
202 : const bool bSwap = bIsLittleEndian;
203 : #endif
204 :
205 0 : if (bSwap && nPixelSize > 1)
206 : {
207 0 : GDALSwapWords(pbyDataToRead, nPixelSize, nBlockXSize * nBlockYSize,
208 : nPixelSize);
209 : }
210 :
211 0 : memcpy(pImage, pbyDataToRead, nExpectedDataSize);
212 : }
213 : else
214 : {
215 0 : int nCurOffset = RASTER_HEADER_SIZE;
216 0 : if (!poRTDS->poRDS->LoadOutdbRaster(
217 : nCurOffset, eDataType, nBand, pbyData, nWKBLength, pImage,
218 : dfTileUpperLeftX, dfTileUpperLeftY, dfTileResX, dfTileResY,
219 : nTileXSize, nTileYSize))
220 : {
221 0 : return CE_Failure;
222 : }
223 : }
224 :
225 0 : return CE_None;
226 : }
|