1 : /*************************************************************************
2 : * File : postgisrasterdataset.cpp
3 : * Project: PostGIS Raster driver
4 : * Purpose: GDAL Dataset implementation for PostGIS Raster driver
5 : * Author: Jorge Arevalo, jorge.arevalo@deimos-space.com
6 : *
7 : * Last changes:
8 : * $Id:$
9 : *
10 : ************************************************************************
11 : * Copyright (c) 2009 - 2011, Jorge Arevalo, jorge.arevalo@deimos-space.com
12 : *
13 : * Permission is hereby granted, free of charge, to any person obtaining a
14 : * copy of this software and associated documentation files (the
15 : * "Software"), to deal in the Software without restriction, including
16 : * without limitation the rights to use, copy, modify, merge, publish,
17 : * distribute, sublicense, and/or sell copies of the Software, and to
18 : * permit persons to whom the Software is furnished to do so, subject to
19 : * the following conditions:
20 : *
21 : * The above copyright notice and this permission notice shall be included
22 : * in all copies or substantial portions of the Software.
23 : *
24 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 : * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
27 : * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
28 : * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
29 : * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
30 : * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 : ************************************************************************/
32 :
33 : #include "postgisraster.h"
34 : #include <stdlib.h>
35 : #include "ogr_api.h"
36 : #include "ogr_geometry.h"
37 : #include "gdal.h"
38 : #include "cpl_conv.h"
39 : #include "cpl_string.h"
40 : #include "gdal_priv.h"
41 : #include <math.h>
42 : #include "cpl_error.h"
43 : #include "ogr_core.h"
44 :
45 : #ifdef _WIN32
46 : #define rint(x) floor((x) + 0.5)
47 : #endif
48 :
49 : CPL_C_START
50 : void GDALRegister_PostGISRaster(void);
51 :
52 : CPL_C_END
53 :
54 :
55 :
56 : /************************
57 : * \brief Constructor
58 : ************************/
59 0 : PostGISRasterDataset::PostGISRasterDataset() {
60 0 : papszSubdatasets = NULL;
61 0 : nSrid = -1;
62 0 : poConn = NULL;
63 0 : bRegularBlocking = false;
64 0 : bRegisteredInRasterColumns = false;
65 0 : pszSchema = NULL;
66 0 : pszTable = NULL;
67 0 : pszColumn = NULL;
68 0 : pszWhere = NULL;
69 0 : pszProjection = NULL;
70 0 : nMode = NO_MODE;
71 0 : poDriver = NULL;
72 0 : nBlockXSize = 0;
73 0 : nBlockYSize = 0;
74 0 : adfGeoTransform[0] = 0.0; /* X Origin (top left corner) */
75 0 : adfGeoTransform[1] = 1.0; /* X Pixel size */
76 0 : adfGeoTransform[2] = 0.0;
77 0 : adfGeoTransform[3] = 0.0; /* Y Origin (top left corner) */
78 0 : adfGeoTransform[4] = 0.0;
79 0 : adfGeoTransform[5] = 1.0; /* Y Pixel Size */
80 0 : bBlocksCached = false;
81 0 : bRegularBlocking = false;
82 0 : bAllTilesSnapToSameGrid = false;
83 :
84 : /**
85 : * TODO: Parametrize bAllTilesSnapToSameGrid. It controls if all the
86 : * raster rows, in ONE_RASTER_PER_TABLE mode, must be checked to test if
87 : * they snap to the same grid and have the same srid. It can be the user
88 : * decission, if he/she's sure all the rows pass the test and want more
89 : * speed.
90 : **/
91 :
92 0 : }
93 :
94 : /************************
95 : * \brief Constructor
96 : ************************/
97 0 : PostGISRasterDataset::~PostGISRasterDataset() {
98 :
99 0 : if (pszSchema)
100 0 : CPLFree(pszSchema);
101 0 : if (pszTable)
102 0 : CPLFree(pszTable);
103 0 : if (pszColumn)
104 0 : CPLFree(pszColumn);
105 0 : if (pszWhere)
106 0 : CPLFree(pszWhere);
107 0 : if (pszProjection)
108 0 : CPLFree(pszProjection);
109 :
110 0 : if (papszSubdatasets)
111 0 : CSLDestroy(papszSubdatasets);
112 :
113 0 : PQfinish(poConn);
114 :
115 : //delete poDriver;
116 0 : }
117 :
118 : /**************************************************************
119 : * \brief Replace the single quotes by " in the input string
120 : *
121 : * Needed before tokenize function
122 : *************************************************************/
123 : static
124 0 : char * ReplaceSingleQuotes(const char * pszInput, int nLength) {
125 : int i;
126 0 : char* pszOutput = NULL;
127 :
128 0 : if (nLength == -1)
129 0 : nLength = strlen(pszInput);
130 :
131 0 : pszOutput = (char*) CPLCalloc(nLength + 1, sizeof (char));
132 :
133 0 : for (i = 0; i < nLength; i++) {
134 0 : if (pszInput[i] == '\'')
135 0 : pszOutput[i] = '"';
136 : else
137 0 : pszOutput[i] = pszInput[i];
138 :
139 : }
140 :
141 0 : return pszOutput;
142 : }
143 :
144 : /**************************************************************
145 : * \brief Replace the quotes by single quotes in the input string
146 : *
147 : * Needed in the 'where' part of the input string
148 : *************************************************************/
149 : static
150 0 : char * ReplaceQuotes(const char * pszInput, int nLength) {
151 : int i;
152 0 : char * pszOutput = NULL;
153 :
154 0 : if (nLength == -1)
155 0 : nLength = strlen(pszInput);
156 :
157 0 : pszOutput = (char*) CPLCalloc(nLength + 1, sizeof (char));
158 :
159 0 : for (i = 0; i < nLength; i++) {
160 0 : if (pszInput[i] == '"')
161 0 : pszOutput[i] = '\'';
162 : else
163 0 : pszOutput[i] = pszInput[i];
164 : }
165 :
166 0 : return pszOutput;
167 : }
168 :
169 : /*****************************************************************************
170 : * \brief Split connection string into user, password, host, database...
171 : *
172 : * The parameters separated by spaces are return as a list of strings. The
173 : * function accepts all the PostgreSQL recognized parameter key words.
174 : *
175 : * The returned list must be freed with CSLDestroy when no longer needed
176 : *
177 : *****************************************************************************/
178 : static
179 0 : char** ParseConnectionString(const char * pszConnectionString) {
180 0 : char * pszEscapedConnectionString = NULL;
181 :
182 : /* Escape string following SQL scheme */
183 0 : pszEscapedConnectionString = ReplaceSingleQuotes(pszConnectionString, -1);
184 :
185 : /* Avoid PG: part */
186 0 : char* pszStartPos = (char*) strstr(pszEscapedConnectionString, ":") + 1;
187 :
188 : /* Tokenize */
189 : char** papszParams = CSLTokenizeString2(pszStartPos, " ",
190 0 : CSLT_HONOURSTRINGS);
191 :
192 : /* Free */
193 0 : CPLFree(pszEscapedConnectionString);
194 :
195 0 : return papszParams;
196 :
197 : }
198 :
199 : /**************************************************************************
200 : * \brief Look for raster tables in database and store them as subdatasets
201 : *
202 : * If no table is provided in connection string, the driver looks for the
203 : * existent raster tables in the schema given as argument. This argument,
204 : * however, is optional. If a NULL value is provided, the driver looks for
205 : * all raster tables in all schemas of the user-provided database.
206 : *
207 : * NOTE: Permissions are managed by libpq. The driver only returns an error
208 : * if an error is returned when trying to access to tables not allowed to
209 : * the current user.
210 : **************************************************************************/
211 0 : GBool PostGISRasterDataset::BrowseDatabase(const char* pszCurrentSchema,
212 : char* pszValidConnectionString) {
213 : /* Be careful! These 3 vars override the class ones! */
214 0 : char* pszSchema = NULL;
215 0 : char* pszTable = NULL;
216 0 : char* pszColumn = NULL;
217 0 : int i = 0;
218 0 : int nTuples = 0;
219 0 : PGresult * poResult = NULL;
220 0 : CPLString osCommand;
221 :
222 :
223 : /*************************************************************
224 : * Fetch all the raster tables and store them as subdatasets
225 : *************************************************************/
226 0 : if (pszCurrentSchema == NULL) {
227 : osCommand.Printf("select pg_namespace.nspname as schema, pg_class.relname as \
228 : table, pg_attribute.attname as column from pg_class, \
229 : pg_namespace,pg_attribute, pg_type where \
230 : pg_class.relnamespace = pg_namespace.oid and pg_class.oid = \
231 : pg_attribute.attrelid and pg_attribute.atttypid = pg_type.oid \
232 0 : and pg_type.typname = 'raster'");
233 :
234 0 : poResult = PQexec(poConn, osCommand.c_str());
235 0 : if (
236 : poResult == NULL ||
237 : PQresultStatus(poResult) != PGRES_TUPLES_OK ||
238 : PQntuples(poResult) <= 0
239 : ) {
240 : CPLError(CE_Failure, CPLE_AppDefined,
241 0 : "Error browsing database for PostGIS Raster tables: %s", PQerrorMessage(poConn));
242 0 : if (poResult != NULL)
243 0 : PQclear(poResult);
244 :
245 0 : return false;
246 : }
247 :
248 :
249 0 : nTuples = PQntuples(poResult);
250 0 : for (i = 0; i < nTuples; i++) {
251 0 : pszSchema = PQgetvalue(poResult, i, 0);
252 0 : pszTable = PQgetvalue(poResult, i, 1);
253 0 : pszColumn = PQgetvalue(poResult, i, 2);
254 :
255 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
256 : CPLSPrintf("SUBDATASET_%d_NAME", (i + 1)),
257 : CPLSPrintf("PG:%s schema=%s table=%s column=%s",
258 0 : pszValidConnectionString, pszSchema, pszTable, pszColumn));
259 :
260 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
261 : CPLSPrintf("SUBDATASET_%d_DESC", (i + 1)),
262 0 : CPLSPrintf("PostGIS Raster table at %s.%s (%s)", pszSchema, pszTable, pszColumn));
263 : }
264 :
265 0 : PQclear(poResult);
266 :
267 : } /**********************************************************************
268 : * Fetch all the schema's raster tables and store them as subdatasets
269 : **********************************************************************/
270 : else {
271 : osCommand.Printf("select pg_class.relname as table, pg_attribute.attname \
272 : as column from pg_class, pg_namespace,pg_attribute, pg_type where \
273 : pg_class.relnamespace = pg_namespace.oid and pg_class.oid = \
274 : pg_attribute.attrelid and pg_attribute.atttypid = pg_type.oid \
275 : and pg_type.typname = 'raster' and pg_namespace.nspname = '%s'",
276 0 : pszCurrentSchema);
277 :
278 0 : poResult = PQexec(poConn, osCommand.c_str());
279 0 : if (
280 : poResult == NULL ||
281 : PQresultStatus(poResult) != PGRES_TUPLES_OK ||
282 : PQntuples(poResult) <= 0
283 : ) {
284 : CPLError(CE_Failure, CPLE_AppDefined,
285 0 : "Error browsing database for PostGIS Raster tables: %s", PQerrorMessage(poConn));
286 0 : if (poResult != NULL)
287 0 : PQclear(poResult);
288 :
289 0 : return false;
290 : }
291 :
292 :
293 0 : nTuples = PQntuples(poResult);
294 0 : for (i = 0; i < nTuples; i++) {
295 0 : pszTable = PQgetvalue(poResult, i, 0);
296 0 : pszColumn = PQgetvalue(poResult, i, 1);
297 :
298 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
299 : CPLSPrintf("SUBDATASET_%d_NAME", (i + 1)),
300 : CPLSPrintf("PG:%s schema=%s table=%s column=%s",
301 0 : pszValidConnectionString, pszCurrentSchema, pszTable, pszColumn));
302 :
303 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
304 : CPLSPrintf("SUBDATASET_%d_DESC", (i + 1)),
305 : CPLSPrintf("PostGIS Raster table at %s.%s (%s)", pszCurrentSchema,
306 0 : pszTable, pszColumn));
307 : }
308 :
309 0 : PQclear(poResult);
310 : }
311 :
312 0 : return true;
313 : }
314 :
315 : /*************************************************************************
316 : * \brief Set the general raster properties.
317 : *
318 : * We must distinguish between tiled and untiled raster coverages. In
319 : * PostGIS Raster, there's no real difference between 'tile' and 'raster'.
320 : * There's only 'raster objects'. Each record of a raster table is a
321 : * raster object, and has its own georeference information, whether if
322 : * the record is a tile of a bigger raster coverage or is a complete
323 : * raster. So, <b>there's no a way of knowing if the rows of a raster
324 : * table are related or not</b>. It's user's responsibility. The only
325 : * thing driver can do is to suppose all the rows of a table are from
326 : * the same raster coverage if the user has queried for one table, without
327 : * specifying a where clause.
328 : *
329 : * The user is responsible to ensure that the raster layer meets the minimum
330 : * topological requirements for analysis. The ideal case is when all the raster
331 : * tiles of a continuous layer are the same size, snap to the same grid and do
332 : * not overlap.
333 : *
334 : * So, when we query for a raster table, we have 3 different cases:
335 : * - If the result is only one row, we can gather the raster properties
336 : * from the returned object, regardless is a tile or a whole raster
337 : * - If the result are several rows of a table, and the working mode is
338 : * ONE_RASTER_PER_TABLE, we assume all the rows are from the same raster
339 : * coverage. The rows are ordered by upper left y, upper left x, growing way,
340 : * and we can get raster size from the first and last elements.
341 : * - If the result are several rows of a table, and the working mode is
342 : * ONE_RASTER_PER_ROW, we assume each row is a different raster object,
343 : * and is reported as a subdataset. If you want only one of the raster rows,
344 : * you must specify a where clause to restrict the number of rows returned.
345 : **************************************************************************/
346 0 : GBool PostGISRasterDataset::SetRasterProperties
347 : (const char * pszValidConnectionString)
348 : {
349 0 : PGresult* poResult = NULL;
350 0 : CPLString osCommand;
351 0 : CPLString osCommand2;
352 0 : PGresult* poResult2 = NULL;
353 0 : int i = 0;
354 0 : int nTuples = 0;
355 0 : GBool bRetValue = false;
356 0 : OGRSpatialReference * poSR = NULL;
357 0 : OGRGeometry* poGeom = NULL;
358 0 : OGRErr OgrErr = OGRERR_NONE;
359 0 : OGREnvelope* poE = NULL;
360 : char* pszExtent;
361 : char* pszProjectionRef;
362 0 : int nTmpSrid = -1;
363 0 : double dfTmpScaleX = 0.0;
364 0 : double dfTmpScaleY = 0.0;
365 0 : double dfTmpSkewX = 0.0;
366 0 : double dfTmpSkewY = 0.0;
367 0 : int nWidth = 0;
368 0 : int nHeight = 0;
369 0 : int nTmpWidth = 0;
370 0 : int nTmpHeight = 0;
371 :
372 :
373 : /* Execute the query to fetch raster data from db */
374 0 : if (pszWhere == NULL) {
375 : osCommand.Printf("select (foo.md).*, foo.rid from (select rid, st_metadata(%s) as md \
376 0 : from %s.%s) as foo", pszColumn, pszSchema, pszTable);
377 : } else {
378 : osCommand.Printf("select (foo.md).*, foo.rid from (select rid, st_metadata(%s) as md \
379 0 : from %s.%s where %s) as foo", pszColumn, pszSchema, pszTable, pszWhere);
380 : }
381 :
382 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
383 0 : "Query: %s", osCommand.c_str());
384 0 : poResult = PQexec(poConn, osCommand.c_str());
385 0 : if (
386 : poResult == NULL ||
387 : PQresultStatus(poResult) != PGRES_TUPLES_OK ||
388 : PQntuples(poResult) <= 0
389 : ) {
390 : CPLError(CE_Failure, CPLE_AppDefined,
391 0 : "Error browsing database for PostGIS Raster properties");
392 0 : if (poResult != NULL)
393 0 : PQclear(poResult);
394 :
395 0 : return false;
396 : }
397 :
398 0 : nTuples = PQntuples(poResult);
399 :
400 :
401 : /******************************************
402 : * Easier case. Only one raster to fetch
403 : ******************************************/
404 0 : if (nTuples == 1) {
405 0 : nSrid = atoi(PQgetvalue(poResult, 0, 8));
406 0 : nBands = atoi(PQgetvalue(poResult, 0, 9));
407 0 : adfGeoTransform[0] = atof(PQgetvalue(poResult, 0, 0)); //upperleft x
408 0 : adfGeoTransform[1] = atof(PQgetvalue(poResult, 0, 4)); //pixelsize x
409 0 : adfGeoTransform[2] = atof(PQgetvalue(poResult, 0, 6)); //skew x
410 0 : adfGeoTransform[3] = atof(PQgetvalue(poResult, 0, 1)); //upperleft y
411 0 : adfGeoTransform[4] = atof(PQgetvalue(poResult, 0, 7)); //skew y
412 0 : adfGeoTransform[5] = atof(PQgetvalue(poResult, 0, 5)); //pixelsize y
413 :
414 0 : nRasterXSize = atoi(PQgetvalue(poResult, 0, 2));
415 0 : nRasterYSize = atoi(PQgetvalue(poResult, 0, 3));
416 :
417 : /**
418 : * Not tiled dataset: The whole raster.
419 : * TODO: 'invent' a good block size.
420 : */
421 0 : nBlockXSize = nRasterXSize;
422 0 : nBlockYSize = nRasterYSize;
423 :
424 0 : bRetValue = true;
425 : } else {
426 0 : switch (nMode) {
427 : /**
428 : * Each row is a different raster. Create subdatasets, one per row
429 : **/
430 :
431 : case ONE_RASTER_PER_ROW:
432 : {
433 :
434 0 : for (i = 0; i < nTuples; i++) {
435 0 : nSrid = atoi(PQgetvalue(poResult, i, 10));
436 :
437 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
438 : CPLSPrintf("SUBDATASET_%d_NAME", (i + 1)),
439 : CPLSPrintf("PG:%s schema=%s table=%s column=%s where='rid = %d'",
440 0 : pszValidConnectionString, pszSchema, pszTable, pszColumn, nSrid));
441 :
442 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
443 : CPLSPrintf("SUBDATASET_%d_DESC", (i + 1)),
444 : CPLSPrintf("PostGIS Raster at %s.%s (%s), rid = %d", pszSchema,
445 0 : pszTable, pszColumn, nSrid));
446 : }
447 :
448 : /* Not a single raster fetched */
449 0 : nRasterXSize = 0;
450 0 : nRasterYSize = 0;
451 :
452 0 : bRetValue = true;
453 :
454 : }
455 0 : break;
456 :
457 : /************************************************************
458 : * All rows form a whole raster coverage
459 : ************************************************************/
460 : case ONE_RASTER_PER_TABLE:
461 : {
462 :
463 : /**
464 : * Get the rest of raster properties from this object
465 : */
466 0 : nSrid = atoi(PQgetvalue(poResult, 0, 8));
467 :
468 0 : nBands = atoi(PQgetvalue(poResult, 0, 9));
469 0 : adfGeoTransform[0] = atof(PQgetvalue(poResult, 0, 0)); //upperleft x
470 0 : adfGeoTransform[1] = atof(PQgetvalue(poResult, 0, 4)); //pixelsize x
471 0 : adfGeoTransform[2] = atof(PQgetvalue(poResult, 0, 6)); //skew x
472 0 : adfGeoTransform[3] = atof(PQgetvalue(poResult, 0, 1)); //upperleft y
473 0 : adfGeoTransform[4] = atof(PQgetvalue(poResult, 0, 7)); //skew y
474 0 : adfGeoTransform[5] = atof(PQgetvalue(poResult, 0, 5)); //pixelsize y
475 0 : nWidth = atoi(PQgetvalue(poResult, 0, 2));
476 0 : nHeight = atoi(PQgetvalue(poResult, 0, 3));
477 :
478 : /**
479 : * Now check if all tiles have the same dimensions.
480 : *
481 : * NOTE: If bRegularBlocking is 'true', this is not checked.
482 : * It's user responsibility
483 : *
484 : * TODO: Find a good block size, that works in any situation.
485 : **/
486 0 : if (!bRegularBlocking)
487 : {
488 0 : for(i = 1; i < nTuples; i++)
489 : {
490 0 : nTmpWidth = atoi(PQgetvalue(poResult, i, 2));
491 0 : nTmpHeight = atoi(PQgetvalue(poResult, i, 3));
492 :
493 0 : if (nWidth != nTmpWidth || nHeight != nTmpHeight)
494 : {
495 : // Not supported until the above TODO is implemented
496 : CPLError(CE_Failure, CPLE_AppDefined,
497 : "Error, the table %s.%s contains tiles with "
498 : "different size, and irregular blocking is "
499 0 : "not supported yet", pszSchema, pszTable);
500 :
501 0 : PQclear(poResult);
502 0 : return false;
503 : }
504 : }
505 :
506 : // Now, we can ensure this
507 0 : bRegularBlocking = true;
508 0 : nBlockXSize = nWidth;
509 0 : nBlockYSize = nHeight;
510 : }
511 :
512 : // The user ensures this...
513 : else
514 : {
515 0 : nBlockXSize = nWidth;
516 0 : nBlockYSize = nHeight;
517 : }
518 :
519 : /**
520 : * Check all the raster tiles have the same srid and snap to the
521 : * same grid. If not, return an error
522 : *
523 : * NOTE: If bAllTilesSnapToSameGrid is 'true', this is not
524 : * checked. It's user responsibility.
525 : *
526 : * TODO: Work even if this requisites are not complained. For
527 : * example, by:
528 : * - Resampling all the rows to the grid of the first one
529 : * - Providing a new grid alignment for all the rows, with a
530 : * maximum of 6 parameters: ulx, uly, pixelsizex, pixelsizey,
531 : * skewx, skewy or a minimum of 3 parameters: ulx, uly,
532 : * pixelsize (x and y pixel sizes are equal and both skew are
533 : * 0).
534 : **/
535 0 : if (!bAllTilesSnapToSameGrid)
536 : {
537 0 : for(i = 1; i < nTuples; i++)
538 : {
539 0 : nTmpSrid = atoi(PQgetvalue(poResult, i, 8));
540 0 : dfTmpScaleX = atof(PQgetvalue(poResult, i, 4));
541 0 : dfTmpScaleY = atof(PQgetvalue(poResult, i, 5));
542 0 : dfTmpSkewX = atof(PQgetvalue(poResult, i, 6));
543 0 : dfTmpSkewY = atof(PQgetvalue(poResult, i, 7));
544 :
545 0 : if (nTmpSrid != nSrid ||
546 0 : FLT_NEQ(dfTmpScaleX, adfGeoTransform[1]) ||
547 0 : FLT_NEQ(dfTmpScaleY, adfGeoTransform[5]) ||
548 0 : FLT_NEQ(dfTmpSkewX, adfGeoTransform[2]) ||
549 0 : FLT_NEQ(dfTmpSkewY, adfGeoTransform[4]))
550 : {
551 : /**
552 : * In this mode, it is not allowed this situation,
553 : * unless while the above TODO is not implemented
554 : **/
555 : CPLError(CE_Failure, CPLE_AppDefined,
556 : "Error, the table %s.%s contains tiles with "
557 : "different SRID or snapping to different grids",
558 0 : pszSchema, pszTable);
559 :
560 0 : PQclear(poResult);
561 0 : return false;
562 : }
563 : }
564 :
565 : // Now, we can ensure this
566 0 : bAllTilesSnapToSameGrid = true;
567 : }
568 :
569 : /**
570 : * Now, if there's irregular blocking and/or the blocks don't
571 : * snap to the same grid or don't have the same srid, we should
572 : * fix these situations. Assuming that we don't return an error
573 : * in that cases, of course.
574 : **/
575 :
576 :
577 :
578 : /**
579 : * Get whole raster extent
580 : **/
581 0 : if (pszWhere == NULL)
582 : osCommand2.Printf("select st_astext(st_setsrid(st_extent(%s::geometry),%d)) from %s.%s",
583 0 : pszColumn, nSrid, pszSchema, pszTable);
584 : else
585 : osCommand2.Printf("select st_astext(st_setsrid(st_extent(%s::geometry),%d)) from %s.%s where %s",
586 0 : pszColumn, nSrid, pszSchema, pszTable, pszWhere);
587 :
588 :
589 0 : poResult2 = PQexec(poConn, osCommand2.c_str());
590 0 : if (poResult2 == NULL ||
591 : PQresultStatus(poResult2) != PGRES_TUPLES_OK ||
592 : PQntuples(poResult2) <= 0) {
593 : CPLError(CE_Failure, CPLE_AppDefined,
594 : "Error calculating whole raster extent: %s",
595 0 : PQerrorMessage(poConn));
596 :
597 0 : if (poResult2 != NULL)
598 0 : PQclear(poResult2);
599 :
600 0 : if (poResult != NULL)
601 0 : PQclear(poResult);
602 :
603 0 : return false;
604 : }
605 :
606 : /* Construct an OGR object with the raster extent */
607 0 : pszExtent = PQgetvalue(poResult2, 0, 0);
608 :
609 0 : pszProjectionRef = (char*) GetProjectionRef();
610 0 : poSR = new OGRSpatialReference(pszProjectionRef);
611 : OgrErr = OGRGeometryFactory::createFromWkt(&pszExtent,
612 0 : poSR, &poGeom);
613 0 : if (OgrErr != OGRERR_NONE) {
614 : CPLError(CE_Failure, CPLE_AppDefined,
615 0 : "Couldn't calculate raster extent");
616 :
617 0 : if (poResult2)
618 0 : PQclear(poResult2);
619 :
620 0 : if (poResult != NULL)
621 0 : PQclear(poResult);
622 :
623 0 : return false;
624 : }
625 :
626 0 : poE = new OGREnvelope();
627 0 : poGeom->getEnvelope(poE);
628 :
629 : /* Correction for upper left y coord*/
630 :
631 : /**
632 : * TODO: Review this. Is a good algorithm?
633 : * If the pixel size Y is negative, we can assume the raster's
634 : * reference system uses cartesian coordinates, in which the
635 : * origin is in lower-left corner, while the origin in an image
636 : * is un upper-left corner. In this case, the upper left Y value
637 : * will be MaxY from the envelope. Otherwise, it will be MinY.
638 : **/
639 : /*
640 : adfGeoTransform[0] = poE->MinX;
641 : if (adfGeoTransform[5] >= 0.0)
642 : adfGeoTransform[3] = poE->MinY;
643 : else
644 : adfGeoTransform[3] = poE->MaxY;
645 : */
646 :
647 : /**
648 : * The raster size is the extent covered for all the raster's
649 : * columns
650 : **/
651 : nRasterXSize = (int)
652 0 : fabs(rint((poE->MaxX - poE->MinX) / adfGeoTransform[1]));
653 : nRasterYSize = (int)
654 0 : fabs(rint((poE->MaxY - poE->MinY) / adfGeoTransform[5]));
655 :
656 :
657 : /* Free resources */
658 0 : OGRGeometryFactory::destroyGeometry(poGeom);
659 0 : delete poE;
660 0 : delete poSR;
661 0 : PQclear(poResult2);
662 :
663 0 : bRetValue = true;
664 :
665 : }
666 0 : break;
667 :
668 : /* TODO: take into account more cases, if applies */
669 : default:
670 : {
671 : CPLError(CE_Failure, CPLE_AppDefined,
672 0 : "Error, incorrect working mode");
673 :
674 0 : bRetValue = false;
675 : }
676 : }
677 : }
678 :
679 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
680 : "adfGeoTransform = {%f, %f, %f, %f, %f,%f}", adfGeoTransform[0],
681 : adfGeoTransform[1], adfGeoTransform[2], adfGeoTransform[3],
682 0 : adfGeoTransform[4], adfGeoTransform[5]);
683 :
684 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
685 0 : "Raster size = (%d, %d)", nRasterXSize, nRasterYSize);
686 :
687 :
688 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
689 0 : "Block dimensions = (%d x %d)", nBlockXSize, nBlockYSize);
690 :
691 0 : PQclear(poResult);
692 0 : return bRetValue;
693 : }
694 :
695 : /*********************************************
696 : * \brief Set raster bands for this dataset
697 : *********************************************/
698 0 : GBool PostGISRasterDataset::SetRasterBands() {
699 0 : GBool bSignedByte = false;
700 0 : int nBitDepth = 8;
701 0 : char* pszDataType = NULL;
702 0 : int iBand = 0;
703 0 : PGresult * poResult = NULL;
704 0 : CPLString osCommand;
705 0 : double dfNodata = 0.0;
706 0 : GDALDataType hDataType = GDT_Byte;
707 0 : int nTuples = 0;
708 0 : GBool bIsOffline = false;
709 :
710 : /* Create each PostGISRasterRasterBand using the band metadata */
711 0 : for (iBand = 0; iBand < nBands; iBand++) {
712 : /* Create query to fetch metadata from db */
713 0 : if (pszWhere == NULL) {
714 : osCommand.Printf("select (foo.md).* from (select"
715 : " distinct st_bandmetadata( %s, %d) as md from %s. %s) as foo",
716 0 : pszColumn, iBand + 1, pszSchema, pszTable);
717 : } else {
718 :
719 : osCommand.Printf("select (foo.md).* from (select"
720 : " distinct st_bandmetadata( %s, %d) as md from %s. %s where %s) as foo",
721 0 : pszColumn, iBand + 1, pszSchema, pszTable, pszWhere);
722 : }
723 :
724 0 : poResult = PQexec(poConn, osCommand.c_str());
725 0 : nTuples = PQntuples(poResult);
726 :
727 : /* Error getting info from database */
728 0 : if (poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK ||
729 : nTuples <= 0) {
730 : CPLError(CE_Failure, CPLE_AppDefined,
731 : "Error getting band metadata: %s",
732 0 : PQerrorMessage(poConn));
733 0 : if (poResult)
734 0 : PQclear(poResult);
735 :
736 0 : return false;
737 : }
738 :
739 : /**
740 : * If we have more than one record here is because there are several
741 : * rows, belonging to the same raster coverage, with different band
742 : * metadata values. An error must be raised.
743 : *
744 : * TODO: Is there any way to fix this problem?
745 : *
746 : * TODO: Even when the difference between metadata values are only a
747 : * few decimal numbers (for example: 3.0000000 and 3.0000001) they're
748 : * different tuples. And in that case, they must be the same
749 : **/
750 : /*
751 : if (nTuples > 1) {
752 : CPLError(CE_Failure, CPLE_AppDefined, "Error, the \
753 : ONE_RASTER_PER_TABLE mode can't be applied if the raster \
754 : rows don't have the same metadata for band %d",
755 : iBand + 1);
756 : PQclear(poResult);
757 : return false;
758 : }
759 : */
760 :
761 :
762 : /* Get metadata and create raster band objects */
763 0 : pszDataType = CPLStrdup(PQgetvalue(poResult, 0, 0));
764 0 : dfNodata = atof(PQgetvalue(poResult, 0, 2));
765 0 : bIsOffline = EQUALN(PQgetvalue(poResult, 0, 3), "t", sizeof (char));
766 :
767 0 : if (EQUALN(pszDataType, "1BB", 3 * sizeof (char))) {
768 0 : hDataType = GDT_Byte;
769 0 : nBitDepth = 1;
770 0 : } else if (EQUALN(pszDataType, "2BUI", 4 * sizeof (char))) {
771 0 : hDataType = GDT_Byte;
772 0 : nBitDepth = 2;
773 0 : } else if (EQUALN(pszDataType, "4BUI", 4 * sizeof (char))) {
774 0 : hDataType = GDT_Byte;
775 0 : nBitDepth = 4;
776 0 : } else if (EQUALN(pszDataType, "8BUI", 4 * sizeof (char))) {
777 0 : hDataType = GDT_Byte;
778 0 : nBitDepth = 8;
779 0 : } else if (EQUALN(pszDataType, "8BSI", 4 * sizeof (char))) {
780 0 : hDataType = GDT_Byte;
781 : /**
782 : * To indicate the unsigned byte values between 128 and 255
783 : * should be interpreted as being values between -128 and -1 for
784 : * applications that recognise the SIGNEDBYTE type.
785 : **/
786 0 : bSignedByte = true;
787 0 : nBitDepth = 8;
788 0 : } else if (EQUALN(pszDataType, "16BSI", 5 * sizeof (char))) {
789 0 : hDataType = GDT_Int16;
790 0 : nBitDepth = 16;
791 0 : } else if (EQUALN(pszDataType, "16BUI", 5 * sizeof (char))) {
792 0 : hDataType = GDT_UInt16;
793 0 : nBitDepth = 16;
794 0 : } else if (EQUALN(pszDataType, "32BSI", 5 * sizeof (char))) {
795 0 : hDataType = GDT_Int32;
796 0 : nBitDepth = 32;
797 0 : } else if (EQUALN(pszDataType, "32BUI", 5 * sizeof (char))) {
798 0 : hDataType = GDT_UInt32;
799 0 : nBitDepth = 32;
800 0 : } else if (EQUALN(pszDataType, "32BF", 4 * sizeof (char))) {
801 0 : hDataType = GDT_Float32;
802 0 : nBitDepth = 32;
803 0 : } else if (EQUALN(pszDataType, "64BF", 4 * sizeof (char))) {
804 0 : hDataType = GDT_Float64;
805 0 : nBitDepth = 64;
806 : } else {
807 0 : hDataType = GDT_Byte;
808 0 : nBitDepth = 8;
809 : }
810 :
811 : /* Create raster band object */
812 : SetBand(iBand + 1, new PostGISRasterRasterBand(this, iBand + 1, hDataType,
813 0 : dfNodata, bSignedByte, nBitDepth, 0, bIsOffline));
814 :
815 0 : CPLFree(pszDataType);
816 0 : PQclear(poResult);
817 : }
818 :
819 0 : return true;
820 : }
821 :
822 : /**
823 : * Read/write a region of image data from multiple bands.
824 : *
825 : * This method allows reading a region of one or more PostGISRasterBands from
826 : * this dataset into a buffer. The write support is still under development
827 : *
828 : * The function fetches all the raster data that intersects with the region
829 : * provided, and store the data in the GDAL cache.
830 : *
831 : * TODO: This only works in case of regular blocking rasters. A more
832 : * general approach to allow non-regular blocking rasters is under development.
833 : *
834 : * It automatically takes care of data type translation if the data type
835 : * (eBufType) of the buffer is different than that of the
836 : * PostGISRasterRasterBand.
837 : *
838 : * TODO: The method should take care of image decimation / replication if the
839 : * buffer size (nBufXSize x nBufYSize) is different than the size of the region
840 : * being accessed (nXSize x nYSize).
841 : *
842 : * The nPixelSpace, nLineSpace and nBandSpace parameters allow reading into or
843 : * writing from various organization of buffers.
844 : *
845 : * @param eRWFlag Either GF_Read to read a region of data, or GF_Write to write
846 : * a region of data.
847 : *
848 : * @param nXOff The pixel offset to the top left corner of the region of the
849 : * band to be accessed. This would be zero to start from the left side.
850 : *
851 : * @param nYOff The line offset to the top left corner of the region of the band
852 : * to be accessed. This would be zero to start from the top.
853 : *
854 : * @param nXSize The width of the region of the band to be accessed in pixels.
855 : *
856 : * @param nYSize The height of the region of the band to be accessed in lines.
857 : *
858 : * @param pData The buffer into which the data should be read, or from which it
859 : * should be written. This buffer must contain at least
860 : * nBufXSize * nBufYSize * nBandCount words of type eBufType. It is organized in
861 : * left to right,top to bottom pixel order. Spacing is controlled by the
862 : * nPixelSpace, and nLineSpace parameters.
863 : *
864 : * @param nBufXSize the width of the buffer image into which the desired region
865 : * is to be read, or from which it is to be written.
866 : *
867 : * @param nBufYSize the height of the buffer image into which the desired region
868 : * is to be read, or from which it is to be written.
869 : *
870 : * @param eBufType the type of the pixel values in the pData data buffer. The
871 : * pixel values will automatically be translated to/from the
872 : * PostGISRasterRasterBand data type as needed.
873 : *
874 : * @param nBandCount the number of bands being read or written.
875 : *
876 : * @param panBandMap the list of nBandCount band numbers being read/written.
877 : * Note band numbers are 1 based. This may be NULL to select the first
878 : * nBandCount bands.
879 : *
880 : * @param nPixelSpace The byte offset from the start of one pixel value in pData
881 : * to the start of the next pixel value within a scanline. If defaulted (0) the
882 : * size of the datatype eBufType is used.
883 : *
884 : * @param nLineSpace The byte offset from the start of one scanline in pData to
885 : * the start of the next. If defaulted (0) the size of the datatype
886 : * eBufType * nBufXSize is used.
887 : *
888 : * @param nBandSpace the byte offset from the start of one bands data to the
889 : * start of the next. If defaulted (0) the value will be nLineSpace * nBufYSize
890 : * implying band sequential organization of the data buffer.
891 : *
892 : * @return CE_Failure if the access fails, otherwise CE_None.
893 : */
894 0 : CPLErr PostGISRasterDataset::IRasterIO(GDALRWFlag eRWFlag,
895 : int nXOff, int nYOff, int nXSize, int nYSize,
896 : void * pData, int nBufXSize, int nBufYSize,
897 : GDALDataType eBufType,
898 : int nBandCount, int *panBandMap,
899 : int nPixelSpace, int nLineSpace, int nBandSpace)
900 : {
901 : double adfTransform[6];
902 : double adfProjWin[8];
903 : int ulx, uly, lrx, lry;
904 0 : CPLString osCommand;
905 0 : PGresult* poResult = NULL;
906 0 : int nTuples = 0;
907 0 : int iTuplesIndex = 0;
908 0 : GByte* pbyData = NULL;
909 0 : int nWKBLength = 0;
910 : int iBandIndex;
911 0 : GDALRasterBlock * poBlock = NULL;
912 : int iBlockXOff, iBlockYOff;
913 : int nBandDataSize, nBandDataLength;
914 0 : char * pBandData = NULL;
915 0 : PostGISRasterRasterBand * poBand = NULL;
916 0 : GByte * pabySrcBlock = NULL;
917 : int nBlocksPerRow, nBlocksPerColumn;
918 : char orderByY[4];
919 : char orderByX[3];
920 : int rid;
921 :
922 :
923 : /**
924 : * TODO: Write support not implemented yet
925 : **/
926 0 : if (eRWFlag == GF_Write)
927 : {
928 : CPLError(CE_Failure, CPLE_NotSupported,
929 0 : "PostGIS Raster does not support writing");
930 0 : return CE_Failure;
931 : }
932 :
933 : /**
934 : * TODO: Data decimation / replication needed
935 : */
936 0 : if (nBufXSize != nXSize || nBufYSize != nYSize)
937 : {
938 : /**
939 : * This will cause individual IReadBlock calls
940 : *
941 : */
942 : return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
943 : pData, nBufXSize, nBufYSize, eBufType, nBandCount,
944 0 : panBandMap, nPixelSpace, nLineSpace, nBandSpace);
945 : }
946 :
947 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO: "
948 : "nBandSpace = %d, nLineSpace = %d, nPixelSpace = %d",
949 0 : nBandSpace, nLineSpace, nPixelSpace);
950 :
951 : /**************************************************************************
952 : * In the first call, we fetch the data from database and store it as
953 : * 'blocks' in the cache.
954 : *
955 : * TODO: If the data is not cached, we must 'invent' a good block size, and
956 : * divide the data in blocks. To get a proper block size, we should rewrite
957 : * the GetBlockSize function at band level.
958 : ***************************************************************************/
959 0 : if (!bBlocksCached)
960 : {
961 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO: "
962 : "Buffer size = (%d, %d), Region size = (%d, %d)",
963 0 : nBufXSize, nBufYSize, nXSize, nYSize);
964 :
965 : /*******************************************************************
966 : * Construct a projected window to intersect the band data
967 : *******************************************************************/
968 0 : GetGeoTransform(adfTransform);
969 0 : ulx = nXOff;
970 0 : uly = nYOff;
971 0 : lrx = nXOff + nXSize;
972 0 : lry = nYOff + nYSize;
973 :
974 : /* Calculate the intersection polygon */
975 0 : adfProjWin[0] = adfTransform[0] +
976 0 : ulx * adfTransform[1] +
977 0 : uly * adfTransform[2];
978 :
979 0 : adfProjWin[1] = adfTransform[3] +
980 0 : ulx * adfTransform[4] +
981 0 : uly * adfTransform[5];
982 :
983 0 : adfProjWin[2] = adfTransform[0] +
984 0 : lrx * adfTransform[1] +
985 0 : uly * adfTransform[2];
986 :
987 0 : adfProjWin[3] = adfTransform[3] +
988 0 : lrx * adfTransform[4] +
989 0 : uly * adfTransform[5];
990 :
991 0 : adfProjWin[4] = adfTransform[0] +
992 0 : lrx * adfTransform[1] +
993 0 : lry * adfTransform[2];
994 :
995 0 : adfProjWin[5] = adfTransform[3] +
996 0 : lrx * adfTransform[4] +
997 0 : lry * adfTransform[5];
998 :
999 0 : adfProjWin[6] = adfTransform[0] +
1000 0 : ulx * adfTransform[1] +
1001 0 : lry * adfTransform[2];
1002 :
1003 0 : adfProjWin[7] = adfTransform[3] +
1004 0 : ulx * adfTransform[4] +
1005 0 : lry * adfTransform[5];
1006 :
1007 :
1008 : /* Construct order by for the query */
1009 0 : memset(orderByX, 0, 3);
1010 0 : memset(orderByY, 0, 4);
1011 :
1012 0 : strcpy(orderByX, "asc");
1013 0 : if (nSrid == -1)
1014 0 : strcpy(orderByY, "asc"); // Y starts at 0 and grows
1015 : else
1016 0 : strcpy(orderByY, "desc");// Y starts at max and decreases
1017 :
1018 :
1019 : /*********************************************************************
1020 : * We first get the data from database (ordered from upper left pixel
1021 : * to lower right one)
1022 : *********************************************************************/
1023 0 : if (pszWhere == NULL)
1024 : {
1025 : osCommand.Printf("SELECT rid, %s, ST_ScaleX(%s), ST_SkewY(%s), "
1026 : "ST_SkewX(%s), ST_ScaleY(%s), ST_UpperLeftX(%s), "
1027 : "ST_UpperLeftY(%s), ST_Width(%s), ST_Height(%s) FROM %s.%s WHERE "
1028 : "ST_Intersects(%s, ST_PolygonFromText('POLYGON((%f %f, %f %f, %f %f"
1029 : ", %f %f, %f %f))', %d)) ORDER BY ST_UpperLeftY(%s) %s, "
1030 : "ST_UpperLeftX(%s) %s", pszColumn, pszColumn,
1031 : pszColumn, pszColumn, pszColumn, pszColumn, pszColumn, pszColumn,
1032 : pszColumn, pszSchema, pszTable, pszColumn, adfProjWin[0],
1033 : adfProjWin[1], adfProjWin[2], adfProjWin[3], adfProjWin[4],
1034 : adfProjWin[5], adfProjWin[6], adfProjWin[7], adfProjWin[0],
1035 0 : adfProjWin[1], nSrid, pszColumn, orderByY, pszColumn, orderByX);
1036 : }
1037 :
1038 :
1039 : else
1040 : {
1041 : osCommand.Printf("SELECT rid, %s ST_ScaleX(%s), ST_SkewY(%s), "
1042 : "ST_SkewX(%s), ST_ScaleY(%s), ST_UpperLeftX(%s), "
1043 : "ST_UpperLeftY(%s), ST_Width(%s), ST_Height(%s) FROM %s.%s WHERE %s AND "
1044 : "ST_Intersects(%s, ST_PolygonFromText('POLYGON((%f %f, %f %f, %f %f"
1045 : ", %f %f, %f %f))', %d)) ORDER BY ST_UpperLeftY(%s) %s, "
1046 : "ST_UpperLeftX(%s) %s", pszColumn, pszColumn,
1047 : pszColumn, pszColumn, pszColumn, pszColumn, pszColumn, pszColumn,
1048 : pszColumn,pszSchema, pszTable, pszWhere, pszColumn, adfProjWin[0],
1049 : adfProjWin[1], adfProjWin[2], adfProjWin[3], adfProjWin[4],
1050 : adfProjWin[5], adfProjWin[6], adfProjWin[7], adfProjWin[0],
1051 0 : adfProjWin[1], nSrid, pszColumn, orderByY, pszColumn, orderByX);
1052 : }
1053 :
1054 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): Query = %s",
1055 0 : osCommand.c_str());
1056 :
1057 0 : poResult = PQexec(poConn, osCommand.c_str());
1058 0 : if (poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK ||
1059 : PQntuples(poResult) <= 0)
1060 : {
1061 0 : if (poResult)
1062 0 : PQclear(poResult);
1063 :
1064 : /**
1065 : * This will cause individual IReadBlock calls
1066 : *
1067 : */
1068 : return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1069 : pData, nBufXSize, nBufYSize, eBufType, nBandCount,
1070 0 : panBandMap, nPixelSpace, nLineSpace, nBandSpace);
1071 : }
1072 :
1073 : /**
1074 : * NOTE: In case of any of the raster columns have different SRID, the
1075 : * query will fail. So, if we don't fail, we can assume all the rows
1076 : * have the same SRID. We don't need to check it
1077 : **/
1078 :
1079 :
1080 0 : nTuples = PQntuples(poResult);
1081 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): nTuples = %d",
1082 0 : nTuples);
1083 :
1084 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): "
1085 0 : "Raster size = (%d, %d)", nRasterXSize, nRasterYSize);
1086 :
1087 :
1088 :
1089 : /**************************************************************************
1090 : * This is the simplest case: all the rows have the same dimensions
1091 : * (regularly blocked raster)
1092 : *
1093 : * Now we'll cache each tuple as a data block. More accurately, we must
1094 : * access each tuple, get the band data, and store this data as a block. So,
1095 : * each tuple contains the data for nBands blocks (and nBandCount <=
1096 : * nBands)
1097 : *************************************************************************/
1098 0 : for(iBandIndex = 0; iBandIndex < nBandCount; iBandIndex++)
1099 : {
1100 0 : poBand = (PostGISRasterRasterBand *)GetRasterBand(iBandIndex + 1);
1101 :
1102 0 : nBandDataSize = GDALGetDataTypeSize(poBand->eDataType) / 8;
1103 :
1104 : nBandDataLength = poBand->nBlockXSize * poBand->nBlockYSize *
1105 0 : nBandDataSize;
1106 :
1107 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): "
1108 : "Block size (%d, %d) for band %d", poBand->nBlockXSize,
1109 0 : poBand->nBlockYSize, poBand->nBand);
1110 :
1111 : /* Enables block caching, if it wasn't enabled */
1112 0 : if (!poBand->InitBlockInfo())
1113 0 : continue;
1114 :
1115 : /**
1116 : * This can be different from poBand->nBlocksPerRow and
1117 : * poBand->nBlocksPerColumn, if the region size is different than
1118 : * the raster size. So, we calculate these values for this case.
1119 : */
1120 : nBlocksPerRow =
1121 0 : (nXSize + poBand->nBlockXSize - 1) / poBand->nBlockXSize;
1122 :
1123 : nBlocksPerColumn =
1124 0 : (nYSize + poBand->nBlockYSize - 1) / poBand->nBlockYSize;
1125 :
1126 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): "
1127 0 : "Number of blocks: %dx%d", nBlocksPerRow, nBlocksPerColumn);
1128 :
1129 0 : for(iBlockYOff = 0; iBlockYOff < nBlocksPerColumn;
1130 : iBlockYOff++)
1131 : {
1132 0 : for(iBlockXOff = 0; iBlockXOff < nBlocksPerRow;
1133 : iBlockXOff++)
1134 : {
1135 : iTuplesIndex = (iBlockYOff * nBlocksPerRow) +
1136 0 : iBlockXOff;
1137 :
1138 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): "
1139 : "iBlockXOff = %d, iBlockYOff = %d, "
1140 : "iTuplesIndex = %d", iBlockXOff, iBlockYOff,
1141 0 : iTuplesIndex);
1142 :
1143 : pbyData = CPLHexToBinary(PQgetvalue(poResult, iTuplesIndex,
1144 0 : 1), &nWKBLength);
1145 :
1146 : pBandData = (char *)GET_BAND_DATA(pbyData, poBand->nBand,
1147 0 : nBandDataSize, nBandDataLength);
1148 :
1149 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): "
1150 : "Block data length for band %d: %d", poBand->nBand,
1151 0 : nBandDataLength);
1152 :
1153 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): "
1154 0 : "Block (%d, %d)", iBlockXOff, iBlockYOff);
1155 :
1156 : /* Create a new block */
1157 : poBlock = new GDALRasterBlock(poBand, iBlockXOff,
1158 0 : iBlockYOff);
1159 :
1160 0 : poBlock->AddLock();
1161 :
1162 : /* Allocate data space */
1163 0 : if (poBlock->Internalize() != CE_None)
1164 : {
1165 0 : poBlock->DropLock();
1166 0 : delete poBlock;
1167 0 : continue;
1168 : }
1169 :
1170 : /* Add the block to the block matrix */
1171 0 : if (poBand->AdoptBlock(iBlockXOff, iBlockYOff, poBlock) !=
1172 : CE_None)
1173 : {
1174 0 : poBlock->DropLock();
1175 0 : delete poBlock;
1176 0 : continue;
1177 : }
1178 :
1179 : /**
1180 : * Copy data to block
1181 : *
1182 : * TODO: Enable write mode too (mark the block as dirty and
1183 : * create IWriteBlock in PostGISRasterRasterBand)
1184 : */
1185 0 : pabySrcBlock = (GByte *)poBlock->GetDataRef();
1186 :
1187 0 : if (poBand->eDataType == eBufType)
1188 : {
1189 0 : memcpy(pabySrcBlock, pBandData, nBandDataLength);
1190 : }
1191 :
1192 : /**
1193 : * As in GDALDataset class... expensive way of handling
1194 : * single words
1195 : */
1196 : else
1197 : {
1198 : GDALCopyWords(pBandData, poBand->eDataType, 0,
1199 0 : pabySrcBlock, eBufType, 0, 1);
1200 : }
1201 :
1202 0 : poBlock->DropLock();
1203 :
1204 0 : CPLFree(pbyData);
1205 0 : pbyData = NULL;
1206 : }
1207 :
1208 : }
1209 :
1210 : }
1211 :
1212 0 : PQclear(poResult);
1213 0 : bBlocksCached = true;
1214 : }
1215 :
1216 : /* Once the blocks are cached, we delegate in GDAL I/O system */
1217 : return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
1218 : nBufXSize, nBufYSize, eBufType, nBandCount, panBandMap, nPixelSpace,
1219 0 : nLineSpace, nBandSpace);
1220 : }
1221 :
1222 : /******************************************************************************
1223 : * \brief Open a connection with PostgreSQL. The connection string will have
1224 : * the PostgreSQL accepted format, plus the next key=value pairs:
1225 : * schema = <schema_name>
1226 : * table = <table_name>
1227 : * column = <column_name>
1228 : * where = <SQL where>
1229 : * mode = <working mode> (1 or 2)
1230 : *
1231 : * These pairs are used for selecting the right raster table.
1232 : *****************************************************************************/
1233 10648 : GDALDataset* PostGISRasterDataset::Open(GDALOpenInfo* poOpenInfo) {
1234 10648 : char** papszParams = NULL;
1235 10648 : char* pszConnectionString = NULL;
1236 10648 : char* pszValidConnectionString = NULL;
1237 10648 : char* pszSchema = NULL;
1238 10648 : char* pszTable = NULL;
1239 10648 : char* pszColumn = NULL;
1240 10648 : char* pszWhere = NULL;
1241 10648 : char* pszTmp = NULL;
1242 10648 : int nMode = -1;
1243 10648 : int nPos = -1;
1244 10648 : int i = 0;
1245 10648 : PGconn * poConn = NULL;
1246 10648 : PostGISRasterDataset* poDS = NULL;
1247 10648 : GBool bBrowseDatabase = false;
1248 10648 : PGresult * poResult = NULL;
1249 10648 : CPLString osCommand;
1250 :
1251 : /**************************
1252 : * Check input parameter
1253 : **************************/
1254 10648 : if (poOpenInfo->pszFilename == NULL ||
1255 : poOpenInfo->fp != NULL ||
1256 : !EQUALN(poOpenInfo->pszFilename, "PG:", 3) ||
1257 : (papszParams = ParseConnectionString(poOpenInfo->pszFilename)) == NULL) {
1258 : /**
1259 : * Drivers must quietly return NULL if the passed file is not of
1260 : * their format. They should only produce an error if the file
1261 : * does appear to be of their supported format, but for some
1262 : * reason, unsupported or corrupt
1263 : */
1264 10648 : return NULL;
1265 : }
1266 :
1267 : /**************************
1268 : * Create driver instance
1269 : **************************/
1270 : /*
1271 : poDriver = (PostGISRasterDriver*) GDALGetDriverByName("PostGISRaster");
1272 : if (poDriver == NULL) {
1273 : CPLError(CE_Failure, CPLE_AppDefined,
1274 : "PostGIS Raster driver not registered");
1275 : return NULL;
1276 : }
1277 : */
1278 :
1279 : /***************************************************************
1280 : * Now check existence of db, schema, table, column and where.
1281 : * If there's no enough data for querying one single table,
1282 : * we'll create a GDAL metadata's list of sub-datasets, with all
1283 : * the table with columns of type 'rasters'
1284 : ***************************************************************/
1285 :
1286 :
1287 : /**************************************************************************
1288 : * Get mode:
1289 : * - 1. ONE_RASTER_PER_ROW: Each row is considered as a separate raster
1290 : * - 2. ONE_RASTER_PER_TABLE: All the table rows are considered as a whole
1291 : * raster coverage
1292 : **************************************************************************/
1293 0 : nPos = CSLFindName(papszParams, "mode");
1294 0 : if (nPos != -1) {
1295 0 : nMode = atoi(CPLParseNameValue(papszParams[nPos], NULL));
1296 :
1297 0 : if (nMode != ONE_RASTER_PER_ROW && nMode != ONE_RASTER_PER_TABLE) {
1298 : /* Unrecognized mode, using default one */
1299 : /*
1300 : CPLError(CE_Warning, CPLE_AppDefined, "Undefined working mode (%d)."
1301 : " Valid working modes are 1 (ONE_RASTER_PER_ROW) and 2"
1302 : " (ONE_RASTER_PER_TABLE). Using ONE_RASTER_PER_TABLE"
1303 : " by default", nMode);
1304 : */
1305 0 : nMode = ONE_RASTER_PER_ROW;
1306 : }
1307 :
1308 : /* Remove the mode from connection string */
1309 0 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1310 : }
1311 : /* Default mode */
1312 : else
1313 0 : nMode = ONE_RASTER_PER_ROW;
1314 :
1315 : /**
1316 : * Case 1: There's no database name: Error, you need, at least,
1317 : * specify a database name (NOTE: insensitive search)
1318 : **/
1319 0 : nPos = CSLFindName(papszParams, "dbname");
1320 0 : if (nPos == -1) {
1321 : CPLError(CE_Failure, CPLE_AppDefined,
1322 0 : "You must specify at least a db name");
1323 0 : return NULL;
1324 : }
1325 :
1326 : /**
1327 : * Case 2: There's database name, but no table name: activate a flag
1328 : * for browsing the database, fetching all the schemas that contain
1329 : * raster tables
1330 : **/
1331 0 : nPos = CSLFindName(papszParams, "table");
1332 0 : if (nPos == -1) {
1333 0 : bBrowseDatabase = true;
1334 :
1335 : /* Get schema name, if exist */
1336 0 : nPos = CSLFindName(papszParams, "schema");
1337 0 : if (nPos != -1) {
1338 0 : pszSchema = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1339 : /* Delete this pair from params array */
1340 0 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1341 : }
1342 :
1343 : /**
1344 : * Remove the rest of the parameters, if exist (they mustn't be present
1345 : * if we want a valid PQ connection string)
1346 : **/
1347 0 : nPos = CSLFindName(papszParams, "column");
1348 0 : if (nPos != -1) {
1349 : /* Delete this pair from params array */
1350 0 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1351 : }
1352 :
1353 0 : nPos = CSLFindName(papszParams, "where");
1354 0 : if (nPos != -1) {
1355 : /* Delete this pair from params array */
1356 0 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1357 : }
1358 : } else {
1359 0 : pszTable = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1360 : /* Delete this pair from params array */
1361 0 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1362 :
1363 : /**
1364 : * Case 3: There's database and table name, but no column
1365 : * name: Use a default column name and use the table to create the
1366 : * dataset
1367 : **/
1368 0 : nPos = CSLFindName(papszParams, "column");
1369 0 : if (nPos == -1) {
1370 0 : pszColumn = CPLStrdup(DEFAULT_COLUMN);
1371 : } /**
1372 : * Case 4: There's database, table and column name: Use the table to
1373 : * create a dataset
1374 : **/
1375 : else {
1376 0 : pszColumn = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1377 : /* Delete this pair from params array */
1378 0 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1379 : }
1380 :
1381 : /* Get the rest of the parameters, if exist */
1382 0 : nPos = CSLFindName(papszParams, "schema");
1383 0 : if (nPos == -1) {
1384 0 : pszSchema = CPLStrdup(DEFAULT_SCHEMA);
1385 : } else {
1386 0 : pszSchema = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1387 : /* Delete this pair from params array */
1388 0 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1389 : }
1390 :
1391 0 : nPos = CSLFindName(papszParams, "where");
1392 0 : if (nPos != -1) {
1393 0 : pszWhere = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1394 : /* Delete this pair from params array */
1395 0 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1396 : }
1397 :
1398 : }
1399 :
1400 : /* Parse pszWhere, if needed */
1401 0 : if (pszWhere) {
1402 0 : pszTmp = ReplaceQuotes(pszWhere, strlen(pszWhere));
1403 0 : CPLFree(pszWhere);
1404 0 : pszWhere = pszTmp;
1405 : }
1406 :
1407 :
1408 : /***************************************
1409 : * Construct a valid connection string
1410 : ***************************************/
1411 : pszValidConnectionString = (char*) CPLCalloc(strlen(poOpenInfo->pszFilename),
1412 0 : sizeof (char));
1413 0 : for (i = 0; i < CSLCount(papszParams); i++) {
1414 0 : pszValidConnectionString = strncat(pszValidConnectionString, papszParams[i], strlen(papszParams[i]));
1415 0 : pszValidConnectionString = strncat(pszValidConnectionString, " ", strlen(" "));
1416 :
1417 : //CPLStrlcat(pszValidConnectionString, papszParams[i], strlen(papszParams[i]));
1418 : //CPLStrlcat(pszValidConnectionString, " ", strlen(" "));
1419 :
1420 : }
1421 :
1422 :
1423 :
1424 : /********************************************************************
1425 : * Open a new database connection
1426 : * TODO: Use enviroment vars (PGHOST, PGPORT, PGUSER) instead of
1427 : * default values.
1428 : * TODO: USE DRIVER INSTEAD OF OPENNING A CONNECTION HERE. MEMORY
1429 : * PROBLEMS DETECTED (SEE DRIVER DESTRUCTOR FOR FURTHER INFORMATION)
1430 : ********************************************************************/
1431 : /*
1432 : poConn = poDriver->GetConnection(pszValidConnectionString,
1433 : CSLFetchNameValueDef(papszParams, "host", DEFAULT_HOST),
1434 : CSLFetchNameValueDef(papszParams, "port", DEFAULT_PORT),
1435 : CSLFetchNameValueDef(papszParams, "user", DEFAULT_USER),
1436 : CSLFetchNameValueDef(papszParams, "password", DEFAULT_PASSWORD));
1437 :
1438 : if (poConn == NULL) {
1439 : CPLError(CE_Failure, CPLE_AppDefined,
1440 : "Couldn't establish a database connection");
1441 : CSLDestroy(papszParams);
1442 : CPLFree(pszConnectionString);
1443 : CPLFree(pszValidConnectionString);
1444 : if (pszSchema)
1445 : CPLFree(pszSchema);
1446 : if (pszTable)
1447 : CPLFree(pszTable);
1448 : if (pszColumn)
1449 : CPLFree(pszColumn);
1450 : if (pszWhere)
1451 : CPLFree(pszWhere);
1452 :
1453 : delete poDriver;
1454 :
1455 : return NULL;
1456 : }
1457 : */
1458 :
1459 :
1460 : /* Frees no longer needed memory */
1461 0 : CSLDestroy(papszParams);
1462 0 : CPLFree(pszConnectionString);
1463 :
1464 : /**
1465 : * Get connection
1466 : * TODO: Try to get connection from poDriver
1467 : **/
1468 0 : poConn = PQconnectdb(pszValidConnectionString);
1469 0 : if (poConn == NULL) {
1470 : CPLError(CE_Failure, CPLE_AppDefined,
1471 0 : "Couldn't establish a database connection");
1472 0 : if (pszSchema)
1473 0 : CPLFree(pszSchema);
1474 0 : if (pszTable)
1475 0 : CPLFree(pszTable);
1476 0 : if (pszColumn)
1477 0 : CPLFree(pszColumn);
1478 0 : if (pszWhere)
1479 0 : CPLFree(pszWhere);
1480 :
1481 0 : return NULL;
1482 :
1483 : }
1484 :
1485 : /* Check geometry type existence */
1486 0 : poResult = PQexec(poConn, "SELECT oid FROM pg_type WHERE typname = 'geometry'");
1487 0 : if (
1488 : poResult == NULL ||
1489 : PQresultStatus(poResult) != PGRES_TUPLES_OK ||
1490 : PQntuples(poResult) <= 0
1491 : ) {
1492 : CPLError(CE_Failure, CPLE_AppDefined,
1493 : "Error checking geometry type existence. Is PostGIS correctly \
1494 0 : installed?: %s", PQerrorMessage(poConn));
1495 0 : if (poResult != NULL)
1496 0 : PQclear(poResult);
1497 0 : if (pszSchema)
1498 0 : CPLFree(pszSchema);
1499 0 : if (pszTable)
1500 0 : CPLFree(pszTable);
1501 0 : if (pszColumn)
1502 0 : CPLFree(pszColumn);
1503 0 : if (pszWhere)
1504 0 : CPLFree(pszWhere);
1505 :
1506 0 : PQfinish(poConn);
1507 :
1508 : //delete poDriver;
1509 :
1510 0 : return NULL;
1511 : }
1512 :
1513 0 : PQclear(poResult);
1514 :
1515 :
1516 : /* Check spatial tables existence */
1517 : poResult = PQexec(poConn, "select pg_namespace.nspname as schemaname, \
1518 : pg_class.relname as tablename from pg_class, \
1519 : pg_namespace where pg_class.relnamespace = pg_namespace.oid \
1520 : and (pg_class.relname='raster_columns' or \
1521 : pg_class.relname='raster_overviews' or \
1522 : pg_class.relname='geometry_columns' or \
1523 0 : pg_class.relname='spatial_ref_sys')");
1524 0 : if (
1525 : poResult == NULL ||
1526 : PQresultStatus(poResult) != PGRES_TUPLES_OK ||
1527 : PQntuples(poResult) <= 0
1528 : ) {
1529 : CPLError(CE_Failure, CPLE_AppDefined,
1530 : "Error checking needed tables existence: %s",
1531 0 : PQerrorMessage(poConn));
1532 0 : if (poResult != NULL)
1533 0 : PQclear(poResult);
1534 0 : if (pszSchema)
1535 0 : CPLFree(pszSchema);
1536 0 : if (pszTable)
1537 0 : CPLFree(pszTable);
1538 0 : if (pszColumn)
1539 0 : CPLFree(pszColumn);
1540 0 : if (pszWhere)
1541 0 : CPLFree(pszWhere);
1542 :
1543 0 : PQfinish(poConn);
1544 :
1545 : //delete poDriver;
1546 :
1547 0 : return NULL;
1548 :
1549 : }
1550 :
1551 0 : PQclear(poResult);
1552 :
1553 : /*************************************************************************
1554 : * No table will be read. Only shows information about the existent raster
1555 : * tables
1556 : *************************************************************************/
1557 0 : if (bBrowseDatabase) {
1558 : /**
1559 : * Creates empty dataset object, only for subdatasets
1560 : **/
1561 0 : poDS = new PostGISRasterDataset();
1562 0 : poDS->poConn = poConn;
1563 0 : poDS->eAccess = GA_ReadOnly;
1564 : //poDS->poDriver = poDriver;
1565 0 : poDS->nMode = (pszSchema) ? BROWSE_SCHEMA : BROWSE_DATABASE;
1566 0 : poDS->nRasterXSize = 0;
1567 0 : poDS->nRasterYSize = 0;
1568 0 : poDS->adfGeoTransform[0] = 0.0;
1569 0 : poDS->adfGeoTransform[1] = 0.0;
1570 0 : poDS->adfGeoTransform[2] = 0.0;
1571 0 : poDS->adfGeoTransform[3] = 0.0;
1572 0 : poDS->adfGeoTransform[4] = 0.0;
1573 0 : poDS->adfGeoTransform[5] = 0.0;
1574 :
1575 : /**
1576 : * Look for raster tables at database and
1577 : * store them as subdatasets
1578 : **/
1579 0 : if (!poDS->BrowseDatabase(pszSchema, pszValidConnectionString)) {
1580 0 : CPLFree(pszValidConnectionString);
1581 0 : delete poDS;
1582 0 : return NULL;
1583 : }
1584 : }
1585 : /***********************************************************************
1586 : * A table will be read: Fetch raster properties from db. Pay attention
1587 : * to the block size: if the raster is blocked at database, the block
1588 : * size can be fetched from each block size, if regular blocking table
1589 : **********************************************************************/
1590 : else {
1591 0 : poDS = new PostGISRasterDataset();
1592 0 : poDS->poConn = poConn;
1593 0 : poDS->eAccess = poOpenInfo->eAccess;
1594 0 : poDS->nMode = nMode;
1595 : //poDS->poDriver = poDriver;
1596 :
1597 0 : poDS->pszSchema = pszSchema;
1598 0 : poDS->pszTable = pszTable;
1599 0 : poDS->pszColumn = pszColumn;
1600 0 : poDS->pszWhere = pszWhere;
1601 :
1602 : /**
1603 : * Fetch basic raster metadata from db
1604 : **/
1605 :
1606 0 : if (!poDS->SetRasterProperties(pszValidConnectionString)) {
1607 0 : CPLFree(pszValidConnectionString);
1608 0 : delete poDS;
1609 0 : return NULL;
1610 : }
1611 :
1612 : /* Set raster bands */
1613 0 : if (!poDS->SetRasterBands()) {
1614 0 : CPLFree(pszValidConnectionString);
1615 0 : delete poDS;
1616 0 : return NULL;
1617 : }
1618 :
1619 : }
1620 :
1621 0 : CPLFree(pszValidConnectionString);
1622 0 : return poDS;
1623 :
1624 : }
1625 :
1626 : /*****************************************
1627 : * \brief Get Metadata from raster
1628 : * TODO: Add more options (the result of
1629 : * calling ST_Metadata, for example)
1630 : *****************************************/
1631 0 : char** PostGISRasterDataset::GetMetadata(const char *pszDomain) {
1632 0 : if (pszDomain != NULL && EQUALN(pszDomain, "SUBDATASETS", 11))
1633 0 : return papszSubdatasets;
1634 : else
1635 0 : return GDALDataset::GetMetadata(pszDomain);
1636 : }
1637 :
1638 : /*****************************************************
1639 : * \brief Fetch the projection definition string
1640 : * for this dataset in OpenGIS WKT format. It should
1641 : * be suitable for use with the OGRSpatialReference
1642 : * class.
1643 : *****************************************************/
1644 0 : const char* PostGISRasterDataset::GetProjectionRef() {
1645 0 : CPLString osCommand;
1646 : PGresult* poResult;
1647 :
1648 0 : if (nSrid == -1)
1649 0 : return "";
1650 :
1651 0 : if (pszProjection)
1652 0 : return pszProjection;
1653 :
1654 : /********************************************************
1655 : * Reading proj from database
1656 : ********************************************************/
1657 : osCommand.Printf("SELECT srtext FROM spatial_ref_sys where SRID=%d",
1658 0 : nSrid);
1659 0 : poResult = PQexec(this->poConn, osCommand.c_str());
1660 0 : if (poResult && PQresultStatus(poResult) == PGRES_TUPLES_OK
1661 : && PQntuples(poResult) > 0) {
1662 : /*
1663 : * TODO: Memory leak detected with valgrind caused by allocation here.
1664 : * Even when the return string is freed
1665 : */
1666 0 : pszProjection = CPLStrdup(PQgetvalue(poResult, 0, 0));
1667 : }
1668 :
1669 0 : if (poResult)
1670 0 : PQclear(poResult);
1671 :
1672 0 : return pszProjection;
1673 : }
1674 :
1675 : /**********************************************************
1676 : * \brief Set projection definition. The input string must
1677 : * be in OGC WKT or PROJ.4 format
1678 : **********************************************************/
1679 0 : CPLErr PostGISRasterDataset::SetProjection(const char * pszProjectionRef) {
1680 0 : VALIDATE_POINTER1(pszProjectionRef, "SetProjection", CE_Failure);
1681 :
1682 0 : CPLString osCommand;
1683 : PGresult * poResult;
1684 0 : int nFetchedSrid = -1;
1685 :
1686 :
1687 : /*****************************************************************
1688 : * Check if the dataset allows updating
1689 : *****************************************************************/
1690 0 : if (GetAccess() != GA_Update) {
1691 : CPLError(CE_Failure, CPLE_NoWriteAccess,
1692 0 : "This driver doesn't allow write access");
1693 0 : return CE_Failure;
1694 : }
1695 :
1696 : /*****************************************************************
1697 : * Look for projection with this text
1698 : *****************************************************************/
1699 :
1700 : // First, WKT text
1701 : osCommand.Printf("SELECT srid FROM spatial_ref_sys where srtext='%s'",
1702 0 : pszProjectionRef);
1703 0 : poResult = PQexec(poConn, osCommand.c_str());
1704 :
1705 0 : if (poResult && PQresultStatus(poResult) == PGRES_TUPLES_OK
1706 : && PQntuples(poResult) > 0) {
1707 :
1708 0 : nFetchedSrid = atoi(PQgetvalue(poResult, 0, 0));
1709 :
1710 : // update class attribute
1711 0 : nSrid = nFetchedSrid;
1712 :
1713 :
1714 : // update raster_columns table
1715 : osCommand.Printf("UPDATE raster_columns SET srid=%d WHERE \
1716 : r_table_name = '%s' AND r_column = '%s'",
1717 0 : nSrid, pszTable, pszColumn);
1718 0 : poResult = PQexec(poConn, osCommand.c_str());
1719 0 : if (poResult == NULL || PQresultStatus(poResult) != PGRES_COMMAND_OK) {
1720 : CPLError(CE_Failure, CPLE_AppDefined,
1721 : "Couldn't update raster_columns table: %s",
1722 0 : PQerrorMessage(poConn));
1723 0 : return CE_Failure;
1724 : }
1725 :
1726 : // TODO: Update ALL blocks with the new srid...
1727 :
1728 0 : return CE_None;
1729 : }
1730 : // If not, proj4 text
1731 : else {
1732 : osCommand.Printf(
1733 : "SELECT srid FROM spatial_ref_sys where proj4text='%s'",
1734 0 : pszProjectionRef);
1735 0 : poResult = PQexec(poConn, osCommand.c_str());
1736 :
1737 0 : if (poResult && PQresultStatus(poResult) == PGRES_TUPLES_OK
1738 : && PQntuples(poResult) > 0) {
1739 :
1740 0 : nFetchedSrid = atoi(PQgetvalue(poResult, 0, 0));
1741 :
1742 : // update class attribute
1743 0 : nSrid = nFetchedSrid;
1744 :
1745 : // update raster_columns table
1746 : osCommand.Printf("UPDATE raster_columns SET srid=%d WHERE \
1747 : r_table_name = '%s' AND r_column = '%s'",
1748 0 : nSrid, pszTable, pszColumn);
1749 :
1750 0 : poResult = PQexec(poConn, osCommand.c_str());
1751 0 : if (poResult == NULL ||
1752 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
1753 : CPLError(CE_Failure, CPLE_AppDefined,
1754 : "Couldn't update raster_columns table: %s",
1755 0 : PQerrorMessage(poConn));
1756 0 : return CE_Failure;
1757 : }
1758 :
1759 : // TODO: Update ALL blocks with the new srid...
1760 :
1761 0 : return CE_None;
1762 : }
1763 : else {
1764 : CPLError(CE_Failure, CPLE_WrongFormat,
1765 0 : "Couldn't find WKT neither proj4 definition");
1766 0 : return CE_Failure;
1767 : }
1768 0 : }
1769 : }
1770 :
1771 : /********************************************************
1772 : * \brief Set the affine transformation coefficients
1773 : ********************************************************/
1774 0 : CPLErr PostGISRasterDataset::SetGeoTransform(double* padfTransform) {
1775 0 : if (!padfTransform)
1776 0 : return CE_Failure;
1777 :
1778 0 : adfGeoTransform[0] = padfTransform[0];
1779 0 : adfGeoTransform[1] = padfTransform[1];
1780 0 : adfGeoTransform[2] = padfTransform[2];
1781 0 : adfGeoTransform[3] = padfTransform[3];
1782 0 : adfGeoTransform[4] = padfTransform[4];
1783 0 : adfGeoTransform[5] = padfTransform[5];
1784 :
1785 0 : return CE_None;
1786 : }
1787 :
1788 : /********************************************************
1789 : * \brief Get the affine transformation coefficients
1790 : ********************************************************/
1791 0 : CPLErr PostGISRasterDataset::GetGeoTransform(double * padfTransform) {
1792 : // copy necessary values in supplied buffer
1793 0 : padfTransform[0] = adfGeoTransform[0];
1794 0 : padfTransform[1] = adfGeoTransform[1];
1795 0 : padfTransform[2] = adfGeoTransform[2];
1796 0 : padfTransform[3] = adfGeoTransform[3];
1797 0 : padfTransform[4] = adfGeoTransform[4];
1798 0 : padfTransform[5] = adfGeoTransform[5];
1799 :
1800 0 : return CE_None;
1801 : }
1802 :
1803 :
1804 : /************************************************************************/
1805 : /* GDALRegister_PostGISRaster() */
1806 :
1807 : /************************************************************************/
1808 558 : void GDALRegister_PostGISRaster() {
1809 : GDALDriver *poDriver;
1810 :
1811 558 : if (GDALGetDriverByName("PostGISRaster") == NULL) {
1812 537 : poDriver = new GDALDriver();
1813 :
1814 537 : poDriver->SetDescription("PostGISRaster");
1815 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
1816 537 : "PostGIS Raster driver");
1817 :
1818 537 : poDriver->pfnOpen = PostGISRasterDataset::Open;
1819 :
1820 537 : GetGDALDriverManager()->RegisterDriver(poDriver);
1821 : }
1822 558 : }
1823 :
|