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