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 0 : }
113 :
114 : /**************************************************************
115 : * \brief Replace the single quotes by " in the input string
116 : *
117 : * Needed before tokenize function
118 : *************************************************************/
119 : static
120 2 : char * ReplaceSingleQuotes(const char * pszInput, int nLength) {
121 : int i;
122 2 : char* pszOutput = NULL;
123 :
124 2 : if (nLength == -1)
125 2 : nLength = strlen(pszInput);
126 :
127 2 : pszOutput = (char*) CPLCalloc(nLength + 1, sizeof (char));
128 :
129 186 : for (i = 0; i < nLength; i++) {
130 184 : if (pszInput[i] == '\'')
131 24 : pszOutput[i] = '"';
132 : else
133 160 : pszOutput[i] = pszInput[i];
134 :
135 : }
136 :
137 2 : return pszOutput;
138 : }
139 :
140 : /**************************************************************
141 : * \brief Replace the quotes by single quotes in the input string
142 : *
143 : * Needed in the 'where' part of the input string
144 : *************************************************************/
145 : static
146 0 : char * ReplaceQuotes(const char * pszInput, int nLength) {
147 : int i;
148 0 : char * pszOutput = NULL;
149 :
150 0 : if (nLength == -1)
151 0 : nLength = strlen(pszInput);
152 :
153 0 : pszOutput = (char*) CPLCalloc(nLength + 1, sizeof (char));
154 :
155 0 : for (i = 0; i < nLength; i++) {
156 0 : if (pszInput[i] == '"')
157 0 : pszOutput[i] = '\'';
158 : else
159 0 : pszOutput[i] = pszInput[i];
160 : }
161 :
162 0 : return pszOutput;
163 : }
164 :
165 : /*****************************************************************************
166 : * \brief Split connection string into user, password, host, database...
167 : *
168 : * The parameters separated by spaces are return as a list of strings. The
169 : * function accepts all the PostgreSQL recognized parameter key words.
170 : *
171 : * The returned list must be freed with CSLDestroy when no longer needed
172 : *
173 : *****************************************************************************/
174 : static
175 2 : char** ParseConnectionString(const char * pszConnectionString) {
176 2 : char * pszEscapedConnectionString = NULL;
177 :
178 : /* Escape string following SQL scheme */
179 2 : pszEscapedConnectionString = ReplaceSingleQuotes(pszConnectionString, -1);
180 :
181 : /* Avoid PG: part */
182 2 : char* pszStartPos = (char*) strstr(pszEscapedConnectionString, ":") + 1;
183 :
184 : /* Tokenize */
185 : char** papszParams = CSLTokenizeString2(pszStartPos, " ",
186 2 : CSLT_HONOURSTRINGS);
187 :
188 : /* Free */
189 2 : CPLFree(pszEscapedConnectionString);
190 :
191 2 : return papszParams;
192 :
193 : }
194 :
195 : /**************************************************************************
196 : * \brief Look for raster tables in database and store them as subdatasets
197 : *
198 : * If no table is provided in connection string, the driver looks for the
199 : * existent raster tables in the schema given as argument. This argument,
200 : * however, is optional. If a NULL value is provided, the driver looks for
201 : * all raster tables in all schemas of the user-provided database.
202 : *
203 : * NOTE: Permissions are managed by libpq. The driver only returns an error
204 : * if an error is returned when trying to access to tables not allowed to
205 : * the current user.
206 : **************************************************************************/
207 0 : GBool PostGISRasterDataset::BrowseDatabase(const char* pszCurrentSchema,
208 : char* pszValidConnectionString) {
209 : /* Be careful! These 3 vars override the class ones! */
210 0 : char* pszSchema = NULL;
211 0 : char* pszTable = NULL;
212 0 : char* pszColumn = NULL;
213 0 : int i = 0;
214 0 : int nTuples = 0;
215 0 : PGresult * poResult = NULL;
216 0 : CPLString osCommand;
217 :
218 :
219 : /*************************************************************
220 : * Fetch all the raster tables and store them as subdatasets
221 : *************************************************************/
222 0 : if (pszCurrentSchema == NULL) {
223 : osCommand.Printf("select pg_namespace.nspname as schema, pg_class.relname as \
224 : table, pg_attribute.attname as column from pg_class, \
225 : pg_namespace,pg_attribute, pg_type where \
226 : pg_class.relnamespace = pg_namespace.oid and pg_class.oid = \
227 : pg_attribute.attrelid and pg_attribute.atttypid = pg_type.oid \
228 0 : and pg_type.typname = 'raster'");
229 :
230 0 : poResult = PQexec(poConn, osCommand.c_str());
231 0 : if (
232 : poResult == NULL ||
233 : PQresultStatus(poResult) != PGRES_TUPLES_OK ||
234 : PQntuples(poResult) <= 0
235 : ) {
236 : CPLError(CE_Failure, CPLE_AppDefined,
237 0 : "Error browsing database for PostGIS Raster tables: %s", PQerrorMessage(poConn));
238 0 : if (poResult != NULL)
239 0 : PQclear(poResult);
240 :
241 0 : return false;
242 : }
243 :
244 :
245 0 : nTuples = PQntuples(poResult);
246 0 : for (i = 0; i < nTuples; i++) {
247 0 : pszSchema = PQgetvalue(poResult, i, 0);
248 0 : pszTable = PQgetvalue(poResult, i, 1);
249 0 : pszColumn = PQgetvalue(poResult, i, 2);
250 :
251 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
252 : CPLSPrintf("SUBDATASET_%d_NAME", (i + 1)),
253 : CPLSPrintf("PG:%s schema=%s table=%s column=%s",
254 0 : pszValidConnectionString, pszSchema, pszTable, pszColumn));
255 :
256 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
257 : CPLSPrintf("SUBDATASET_%d_DESC", (i + 1)),
258 0 : CPLSPrintf("PostGIS Raster table at %s.%s (%s)", pszSchema, pszTable, pszColumn));
259 : }
260 :
261 0 : PQclear(poResult);
262 :
263 : }
264 : /**********************************************************************
265 : * Fetch all the schema's raster tables and store them as subdatasets
266 : **********************************************************************/
267 : else {
268 : osCommand.Printf("select pg_class.relname as table, pg_attribute.attname \
269 : as column from pg_class, pg_namespace,pg_attribute, pg_type where \
270 : pg_class.relnamespace = pg_namespace.oid and pg_class.oid = \
271 : pg_attribute.attrelid and pg_attribute.atttypid = pg_type.oid \
272 : and pg_type.typname = 'raster' and pg_namespace.nspname = '%s'",
273 0 : pszCurrentSchema);
274 :
275 0 : poResult = PQexec(poConn, osCommand.c_str());
276 0 : if (
277 : poResult == NULL ||
278 : PQresultStatus(poResult) != PGRES_TUPLES_OK ||
279 : PQntuples(poResult) <= 0
280 : ) {
281 : CPLError(CE_Failure, CPLE_AppDefined,
282 0 : "Error browsing database for PostGIS Raster tables: %s", PQerrorMessage(poConn));
283 0 : if (poResult != NULL)
284 0 : PQclear(poResult);
285 :
286 0 : return false;
287 : }
288 :
289 :
290 0 : nTuples = PQntuples(poResult);
291 0 : for (i = 0; i < nTuples; i++) {
292 0 : pszTable = PQgetvalue(poResult, i, 0);
293 0 : pszColumn = PQgetvalue(poResult, i, 1);
294 :
295 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
296 : CPLSPrintf("SUBDATASET_%d_NAME", (i + 1)),
297 : CPLSPrintf("PG:%s schema=%s table=%s column=%s",
298 0 : pszValidConnectionString, pszCurrentSchema, pszTable, pszColumn));
299 :
300 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
301 : CPLSPrintf("SUBDATASET_%d_DESC", (i + 1)),
302 : CPLSPrintf("PostGIS Raster table at %s.%s (%s)", pszCurrentSchema,
303 0 : pszTable, pszColumn));
304 : }
305 :
306 0 : PQclear(poResult);
307 : }
308 :
309 0 : return true;
310 : }
311 :
312 : /*************************************************************************
313 : * \brief Set the general raster properties.
314 : *
315 : * We must distinguish between tiled and untiled raster coverages. In
316 : * PostGIS Raster, there's no real difference between 'tile' and 'raster'.
317 : * There's only 'raster objects'. Each record of a raster table is a
318 : * raster object, and has its own georeference information, whether if
319 : * the record is a tile of a bigger raster coverage or is a complete
320 : * raster. So, <b>there's no a way of knowing if the rows of a raster
321 : * table are related or not</b>. It's user's responsibility. The only
322 : * thing driver can do is to suppose all the rows of a table are from
323 : * the same raster coverage if the user has queried for one table, without
324 : * specifying a where clause.
325 : *
326 : * The user is responsible to ensure that the raster layer meets the minimum
327 : * topological requirements for analysis. The ideal case is when all the raster
328 : * tiles of a continuous layer are the same size, snap to the same grid and do
329 : * not overlap.
330 : *
331 : * So, when we query for a raster table, we have 3 different cases:
332 : * - If the result is only one row, we can gather the raster properties
333 : * from the returned object, regardless is a tile or a whole raster
334 : * - If the result are several rows of a table, and the working mode is
335 : * ONE_RASTER_PER_TABLE, we assume all the rows are from the same raster
336 : * coverage. The rows are ordered by upper left y, upper left x, growing way,
337 : * and we can get raster size from the first and last elements.
338 : * - If the result are several rows of a table, and the working mode is
339 : * ONE_RASTER_PER_ROW, we assume each row is a different raster object,
340 : * and is reported as a subdataset. If you want only one of the raster rows,
341 : * you must specify a where clause to restrict the number of rows returned.
342 : **************************************************************************/
343 0 : GBool PostGISRasterDataset::SetRasterProperties
344 : (const char * pszValidConnectionString)
345 : {
346 0 : PGresult* poResult = NULL;
347 0 : CPLString osCommand;
348 0 : CPLString osCommand2;
349 0 : PGresult* poResult2 = NULL;
350 0 : int i = 0;
351 0 : int nTuples = 0;
352 0 : GBool bRetValue = false;
353 0 : OGRSpatialReference * poSR = NULL;
354 0 : OGRGeometry* poGeom = NULL;
355 0 : OGRErr OgrErr = OGRERR_NONE;
356 0 : OGREnvelope* poE = NULL;
357 : char* pszExtent;
358 : char* pszProjectionRef;
359 0 : char* pszIdColumn = NULL;
360 0 : int nTmpSrid = -1;
361 0 : double dfTmpScaleX = 0.0;
362 0 : double dfTmpScaleY = 0.0;
363 0 : double dfTmpSkewX = 0.0;
364 0 : double dfTmpSkewY = 0.0;
365 0 : int nWidth = 0;
366 0 : int nHeight = 0;
367 0 : int nTmpWidth = 0;
368 0 : int nTmpHeight = 0;
369 :
370 : /* Determine the primary key/unique column on the raster table */
371 : osCommand.Printf("select cols.column_name from information_schema."
372 : "constraint_column_usage as cols join information_schema."
373 : "table_constraints as constr on constr.constraint_name = cols."
374 : "constraint_name where cols.table_schema = '%s' and cols.table_name "
375 0 : "= '%s'", pszSchema, pszTable);
376 :
377 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
378 0 : "Query: %s", osCommand.c_str());
379 0 : poResult = PQexec(poConn, osCommand.c_str());
380 0 : if (poResult == NULL ||
381 : PQresultStatus(poResult) != PGRES_TUPLES_OK ||
382 : PQntuples(poResult) <= 0 ) {
383 :
384 0 : PQclear(poResult);
385 :
386 : // maybe there is no primary key or unique constraint;
387 : // a sequence will also suffice, so look for the first sequence
388 :
389 : osCommand.Printf("select cols.column_name from information_schema."
390 : "columns as cols join information_schema.sequences as seqs on cols."
391 : "column_default like '%%'||seqs.sequence_name||'%%' where cols."
392 : "table_schema = '%s' and cols.table_name = '%s'", pszSchema,
393 0 : pszTable);
394 :
395 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
396 0 : "Query: %s", osCommand.c_str());
397 0 : poResult = PQexec(poConn, osCommand.c_str());
398 :
399 0 : if (poResult == NULL ||
400 : PQresultStatus(poResult) != PGRES_TUPLES_OK ||
401 : PQntuples(poResult) <= 0) {
402 :
403 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
404 0 : "Could not find a primary key or unique column on the specified table; using UpperLeftX and UpperLeftY.");
405 :
406 : /* If no primary key or unique column found, fall back to raster upperleft x&y */
407 : }
408 : else {
409 0 : pszIdColumn = CPLStrdup(PQgetvalue(poResult, 0, 0));
410 : }
411 : }
412 : else {
413 0 : pszIdColumn = CPLStrdup(PQgetvalue(poResult, 0, 0));
414 : }
415 :
416 0 : PQclear(poResult);
417 :
418 : /* Execute the query to fetch raster data from db */
419 0 : if (pszWhere == NULL) {
420 0 : if (pszIdColumn == NULL) {
421 : osCommand.Printf("select (foo.md).* from (select st_metadata(%s) "
422 0 : "as md from %s.%s) as foo", pszColumn, pszSchema, pszTable);
423 : }
424 : else {
425 : osCommand.Printf("select (foo.md).*, foo.%s from (select %s, "
426 : "st_metadata(%s) as md from %s.%s) as foo", pszIdColumn,
427 0 : pszIdColumn, pszColumn, pszSchema, pszTable);
428 : }
429 : } else {
430 0 : if (pszIdColumn == NULL) {
431 : osCommand.Printf("select (foo.md).* from (select st_metadata(%s) "
432 : "as md from %s.%s where %s) as foo", pszColumn, pszSchema,
433 0 : pszTable, pszWhere);
434 : }
435 : else {
436 : osCommand.Printf("select (foo.md).*, foo.%s from (select %s, "
437 : "st_metadata(%s) as md from %s.%s where %s) as foo", pszIdColumn,
438 0 : pszIdColumn, pszColumn, pszSchema, pszTable, pszWhere);
439 : }
440 : }
441 :
442 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
443 0 : "Query: %s", osCommand.c_str());
444 0 : poResult = PQexec(poConn, osCommand.c_str());
445 0 : if (
446 : poResult == NULL ||
447 : PQresultStatus(poResult) != PGRES_TUPLES_OK ||
448 : PQntuples(poResult) <= 0
449 : ) {
450 : CPLError(CE_Failure, CPLE_AppDefined,
451 0 : "Error browsing database for PostGIS Raster properties");
452 :
453 0 : if (poResult != NULL)
454 0 : PQclear(poResult);
455 :
456 0 : if (pszIdColumn != NULL)
457 0 : CPLFree(pszIdColumn);
458 :
459 0 : return false;
460 : }
461 :
462 0 : nTuples = PQntuples(poResult);
463 :
464 :
465 : /******************************************
466 : * Easier case. Only one raster to fetch
467 : ******************************************/
468 0 : if (nTuples == 1) {
469 0 : nSrid = atoi(PQgetvalue(poResult, 0, 8));
470 0 : nBands = atoi(PQgetvalue(poResult, 0, 9));
471 0 : adfGeoTransform[0] = atof(PQgetvalue(poResult, 0, 0)); //upperleft x
472 0 : adfGeoTransform[1] = atof(PQgetvalue(poResult, 0, 4)); //pixelsize x
473 0 : adfGeoTransform[2] = atof(PQgetvalue(poResult, 0, 6)); //skew x
474 0 : adfGeoTransform[3] = atof(PQgetvalue(poResult, 0, 1)); //upperleft y
475 0 : adfGeoTransform[4] = atof(PQgetvalue(poResult, 0, 7)); //skew y
476 0 : adfGeoTransform[5] = atof(PQgetvalue(poResult, 0, 5)); //pixelsize y
477 :
478 0 : nRasterXSize = atoi(PQgetvalue(poResult, 0, 2));
479 0 : nRasterYSize = atoi(PQgetvalue(poResult, 0, 3));
480 :
481 : /**
482 : * Not tiled dataset: The whole raster.
483 : * TODO: 'invent' a good block size.
484 : */
485 0 : nBlockXSize = nRasterXSize;
486 0 : nBlockYSize = nRasterYSize;
487 :
488 0 : bRetValue = true;
489 : } else {
490 0 : switch (nMode) {
491 : /**
492 : * Each row is a different raster. Create subdatasets, one per row
493 : **/
494 :
495 : case ONE_RASTER_PER_ROW:
496 : {
497 :
498 0 : if (pszIdColumn == NULL) {
499 0 : for (i = 0; i < nTuples; i++) {
500 0 : adfGeoTransform[0] = atof(PQgetvalue(poResult, i, 0)); //upperleft x
501 0 : adfGeoTransform[3] = atof(PQgetvalue(poResult, i, 1)); //upperleft y
502 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
503 : CPLSPrintf("SUBDATASET_%d_NAME", (i + 1)),
504 : CPLSPrintf("PG:%s schema=%s table=%s column=%s "
505 : "where='ST_UpperLeftX(%s) = %f AND ST_UpperLeftY(%s) = %f'",
506 : pszValidConnectionString, pszSchema, pszTable, pszColumn,
507 0 : pszColumn, adfGeoTransform[0], pszColumn, adfGeoTransform[3]));
508 :
509 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
510 : CPLSPrintf("SUBDATASET_%d_DESC", (i + 1)),
511 : CPLSPrintf("PostGIS Raster at %s.%s (%s), UpperLeft = %f, %f", pszSchema,
512 0 : pszTable, pszColumn, adfGeoTransform[0], adfGeoTransform[3]));
513 : }
514 : }
515 : else {
516 0 : for (i = 0; i < nTuples; i++) {
517 : // this is the raster ID (or unique column)
518 0 : nSrid = atoi(PQgetvalue(poResult, i, 10));
519 :
520 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
521 : CPLSPrintf("SUBDATASET_%d_NAME", (i + 1)),
522 : CPLSPrintf("PG:%s schema=%s table=%s column=%s where='%s = %d'",
523 : pszValidConnectionString, pszSchema, pszTable, pszColumn,
524 0 : pszIdColumn, nSrid));
525 :
526 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
527 : CPLSPrintf("SUBDATASET_%d_DESC", (i + 1)),
528 : CPLSPrintf("PostGIS Raster at %s.%s (%s), %s = %d", pszSchema,
529 0 : pszTable, pszColumn, pszIdColumn, nSrid));
530 : }
531 :
532 0 : CPLFree(pszIdColumn);
533 : }
534 :
535 :
536 : /* Not a single raster fetched */
537 0 : nRasterXSize = 0;
538 0 : nRasterYSize = 0;
539 :
540 0 : bRetValue = true;
541 :
542 : }
543 0 : break;
544 :
545 : /************************************************************
546 : * All rows form a whole raster coverage
547 : ************************************************************/
548 : case ONE_RASTER_PER_TABLE:
549 : {
550 : /* This column is not used in this mode. */
551 0 : if (pszIdColumn != NULL)
552 0 : CPLFree(pszIdColumn);
553 :
554 : /**
555 : * Get the rest of raster properties from this object
556 : */
557 0 : nSrid = atoi(PQgetvalue(poResult, 0, 8));
558 :
559 0 : nBands = atoi(PQgetvalue(poResult, 0, 9));
560 0 : adfGeoTransform[0] = atof(PQgetvalue(poResult, 0, 0)); //upperleft x
561 0 : adfGeoTransform[1] = atof(PQgetvalue(poResult, 0, 4)); //pixelsize x
562 0 : adfGeoTransform[2] = atof(PQgetvalue(poResult, 0, 6)); //skew x
563 0 : adfGeoTransform[3] = atof(PQgetvalue(poResult, 0, 1)); //upperleft y
564 0 : adfGeoTransform[4] = atof(PQgetvalue(poResult, 0, 7)); //skew y
565 0 : adfGeoTransform[5] = atof(PQgetvalue(poResult, 0, 5)); //pixelsize y
566 0 : nWidth = atoi(PQgetvalue(poResult, 0, 2));
567 0 : nHeight = atoi(PQgetvalue(poResult, 0, 3));
568 :
569 : /**
570 : * Now check if all tiles have the same dimensions.
571 : *
572 : * NOTE: If bRegularBlocking is 'true', this is not checked.
573 : * It's user responsibility
574 : *
575 : * TODO: Find a good block size, that works in any situation.
576 : **/
577 0 : if (!bRegularBlocking)
578 : {
579 0 : for(i = 1; i < nTuples; i++)
580 : {
581 0 : nTmpWidth = atoi(PQgetvalue(poResult, i, 2));
582 0 : nTmpHeight = atoi(PQgetvalue(poResult, i, 3));
583 :
584 0 : if (nWidth != nTmpWidth || nHeight != nTmpHeight)
585 : {
586 : // Not supported until the above TODO is implemented
587 : CPLError(CE_Failure, CPLE_AppDefined,
588 : "Error, the table %s.%s contains tiles with "
589 : "different size, and irregular blocking is "
590 0 : "not supported yet", pszSchema, pszTable);
591 :
592 0 : PQclear(poResult);
593 :
594 0 : return false;
595 : }
596 : }
597 :
598 : // Now, we can ensure this
599 0 : bRegularBlocking = true;
600 0 : nBlockXSize = nWidth;
601 0 : nBlockYSize = nHeight;
602 : }
603 :
604 : // The user ensures this...
605 : else
606 : {
607 0 : nBlockXSize = nWidth;
608 0 : nBlockYSize = nHeight;
609 : }
610 :
611 : /**
612 : * Check all the raster tiles have the same srid and snap to the
613 : * same grid. If not, return an error
614 : *
615 : * NOTE: If bAllTilesSnapToSameGrid is 'true', this is not
616 : * checked. It's user responsibility.
617 : *
618 : * TODO: Work even if this requisites are not complained. For
619 : * example, by:
620 : * - Resampling all the rows to the grid of the first one
621 : * - Providing a new grid alignment for all the rows, with a
622 : * maximum of 6 parameters: ulx, uly, pixelsizex, pixelsizey,
623 : * skewx, skewy or a minimum of 3 parameters: ulx, uly,
624 : * pixelsize (x and y pixel sizes are equal and both skew are
625 : * 0).
626 : **/
627 0 : if (!bAllTilesSnapToSameGrid)
628 : {
629 0 : for(i = 1; i < nTuples; i++)
630 : {
631 0 : nTmpSrid = atoi(PQgetvalue(poResult, i, 8));
632 0 : dfTmpScaleX = atof(PQgetvalue(poResult, i, 4));
633 0 : dfTmpScaleY = atof(PQgetvalue(poResult, i, 5));
634 0 : dfTmpSkewX = atof(PQgetvalue(poResult, i, 6));
635 0 : dfTmpSkewY = atof(PQgetvalue(poResult, i, 7));
636 :
637 0 : if (nTmpSrid != nSrid ||
638 0 : FLT_NEQ(dfTmpScaleX, adfGeoTransform[1]) ||
639 0 : FLT_NEQ(dfTmpScaleY, adfGeoTransform[5]) ||
640 0 : FLT_NEQ(dfTmpSkewX, adfGeoTransform[2]) ||
641 0 : FLT_NEQ(dfTmpSkewY, adfGeoTransform[4]))
642 : {
643 : /**
644 : * In this mode, it is not allowed this situation,
645 : * unless while the above TODO is not implemented
646 : **/
647 : CPLError(CE_Failure, CPLE_AppDefined,
648 : "Error, the table %s.%s contains tiles with "
649 : "different SRID or snapping to different grids",
650 0 : pszSchema, pszTable);
651 :
652 0 : PQclear(poResult);
653 :
654 0 : return false;
655 : }
656 : }
657 :
658 : // Now, we can ensure this
659 0 : bAllTilesSnapToSameGrid = true;
660 : }
661 :
662 : /**
663 : * Now, if there's irregular blocking and/or the blocks don't
664 : * snap to the same grid or don't have the same srid, we should
665 : * fix these situations. Assuming that we don't return an error
666 : * in that cases, of course.
667 : **/
668 :
669 :
670 :
671 : /**
672 : * Get whole raster extent
673 : **/
674 0 : if (pszWhere == NULL)
675 : osCommand2.Printf("select st_astext(st_setsrid(st_extent(%s::geometry),%d)) from %s.%s",
676 0 : pszColumn, nSrid, pszSchema, pszTable);
677 : else
678 : osCommand2.Printf("select st_astext(st_setsrid(st_extent(%s::geometry),%d)) from %s.%s where %s",
679 0 : pszColumn, nSrid, pszSchema, pszTable, pszWhere);
680 :
681 :
682 0 : poResult2 = PQexec(poConn, osCommand2.c_str());
683 0 : if (poResult2 == NULL ||
684 : PQresultStatus(poResult2) != PGRES_TUPLES_OK ||
685 : PQntuples(poResult2) <= 0) {
686 : CPLError(CE_Failure, CPLE_AppDefined,
687 : "Error calculating whole raster extent: %s",
688 0 : PQerrorMessage(poConn));
689 :
690 0 : if (poResult2 != NULL)
691 0 : PQclear(poResult2);
692 :
693 0 : if (poResult != NULL)
694 0 : PQclear(poResult);
695 :
696 0 : return false;
697 : }
698 :
699 : /* Construct an OGR object with the raster extent */
700 0 : pszExtent = PQgetvalue(poResult2, 0, 0);
701 :
702 0 : pszProjectionRef = (char*) GetProjectionRef();
703 0 : poSR = new OGRSpatialReference(pszProjectionRef);
704 : OgrErr = OGRGeometryFactory::createFromWkt(&pszExtent,
705 0 : poSR, &poGeom);
706 0 : if (OgrErr != OGRERR_NONE) {
707 : CPLError(CE_Failure, CPLE_AppDefined,
708 0 : "Couldn't calculate raster extent");
709 :
710 0 : if (poResult2)
711 0 : PQclear(poResult2);
712 :
713 0 : if (poResult != NULL)
714 0 : PQclear(poResult);
715 :
716 0 : return false;
717 : }
718 :
719 0 : poE = new OGREnvelope();
720 0 : poGeom->getEnvelope(poE);
721 :
722 : /* Correction for upper left y coord*/
723 :
724 : /**
725 : * TODO: Review this. Is a good algorithm?
726 : * If the pixel size Y is negative, we can assume the raster's
727 : * reference system uses cartesian coordinates, in which the
728 : * origin is in lower-left corner, while the origin in an image
729 : * is un upper-left corner. In this case, the upper left Y value
730 : * will be MaxY from the envelope. Otherwise, it will be MinY.
731 : **/
732 : /*
733 : adfGeoTransform[0] = poE->MinX;
734 : if (adfGeoTransform[5] >= 0.0)
735 : adfGeoTransform[3] = poE->MinY;
736 : else
737 : adfGeoTransform[3] = poE->MaxY;
738 : */
739 :
740 : /**
741 : * The raster size is the extent covered for all the raster's
742 : * columns
743 : **/
744 : nRasterXSize = (int)
745 0 : fabs(rint((poE->MaxX - poE->MinX) / adfGeoTransform[1]));
746 : nRasterYSize = (int)
747 0 : fabs(rint((poE->MaxY - poE->MinY) / adfGeoTransform[5]));
748 :
749 :
750 : /* Free resources */
751 0 : OGRGeometryFactory::destroyGeometry(poGeom);
752 0 : delete poE;
753 0 : delete poSR;
754 0 : PQclear(poResult2);
755 :
756 0 : bRetValue = true;
757 :
758 : }
759 0 : break;
760 :
761 : /* TODO: take into account more cases, if applies */
762 : default:
763 : {
764 : CPLError(CE_Failure, CPLE_AppDefined,
765 0 : "Error, incorrect working mode");
766 :
767 : /* This column is not used in this mode. */
768 0 : if (pszIdColumn != NULL)
769 0 : CPLFree(pszIdColumn);
770 :
771 0 : bRetValue = false;
772 : }
773 : }
774 : }
775 :
776 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
777 : "adfGeoTransform = {%f, %f, %f, %f, %f,%f}", adfGeoTransform[0],
778 : adfGeoTransform[1], adfGeoTransform[2], adfGeoTransform[3],
779 0 : adfGeoTransform[4], adfGeoTransform[5]);
780 :
781 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
782 0 : "Raster size = (%d, %d)", nRasterXSize, nRasterYSize);
783 :
784 :
785 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
786 0 : "Block dimensions = (%d x %d)", nBlockXSize, nBlockYSize);
787 :
788 0 : PQclear(poResult);
789 0 : return bRetValue;
790 : }
791 :
792 : /*********************************************
793 : * \brief Set raster bands for this dataset
794 : *********************************************/
795 0 : GBool PostGISRasterDataset::SetRasterBands() {
796 0 : GBool bSignedByte = false;
797 0 : int nBitDepth = 8;
798 0 : char* pszDataType = NULL;
799 0 : int iBand = 0;
800 0 : PGresult * poResult = NULL;
801 0 : CPLString osCommand;
802 0 : double dfNodata = 0.0;
803 0 : GDALDataType hDataType = GDT_Byte;
804 0 : int nTuples = 0;
805 0 : GBool bIsOffline = false;
806 0 : GBool bHasNoDataValue = false;
807 :
808 : /* Create each PostGISRasterRasterBand using the band metadata */
809 0 : for (iBand = 0; iBand < nBands; iBand++) {
810 : /* Create query to fetch metadata from db */
811 0 : if (pszWhere == NULL) {
812 : osCommand.Printf("select (foo.md).* from (select"
813 : " distinct st_bandmetadata( %s, %d) as md from %s. %s) as foo",
814 0 : pszColumn, iBand + 1, pszSchema, pszTable);
815 : } else {
816 :
817 : osCommand.Printf("select (foo.md).* from (select"
818 : " distinct st_bandmetadata( %s, %d) as md from %s. %s where %s) as foo",
819 0 : pszColumn, iBand + 1, pszSchema, pszTable, pszWhere);
820 : }
821 :
822 0 : poResult = PQexec(poConn, osCommand.c_str());
823 0 : nTuples = PQntuples(poResult);
824 :
825 : /* Error getting info from database */
826 0 : if (poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK ||
827 : nTuples <= 0) {
828 : CPLError(CE_Failure, CPLE_AppDefined,
829 : "Error getting band metadata: %s",
830 0 : PQerrorMessage(poConn));
831 0 : if (poResult)
832 0 : PQclear(poResult);
833 :
834 0 : return false;
835 : }
836 :
837 : /**
838 : * If we have more than one record here is because there are several
839 : * rows, belonging to the same raster coverage, with different band
840 : * metadata values. An error must be raised.
841 : *
842 : * TODO: Is there any way to fix this problem?
843 : *
844 : * TODO: Even when the difference between metadata values are only a
845 : * few decimal numbers (for example: 3.0000000 and 3.0000001) they're
846 : * different tuples. And in that case, they must be the same
847 : **/
848 : /*
849 : if (nTuples > 1) {
850 : CPLError(CE_Failure, CPLE_AppDefined, "Error, the \
851 : ONE_RASTER_PER_TABLE mode can't be applied if the raster \
852 : rows don't have the same metadata for band %d",
853 : iBand + 1);
854 : PQclear(poResult);
855 : return false;
856 : }
857 : */
858 :
859 :
860 : /* Get metadata and create raster band objects */
861 0 : pszDataType = CPLStrdup(PQgetvalue(poResult, 0, 0));
862 0 : bHasNoDataValue = EQUALN(PQgetvalue(poResult, 0, 1), "t", sizeof(char));
863 0 : dfNodata = atof(PQgetvalue(poResult, 0, 2));
864 0 : bIsOffline = EQUALN(PQgetvalue(poResult, 0, 3), "t", sizeof (char));
865 :
866 0 : if (EQUALN(pszDataType, "1BB", 3 * sizeof (char))) {
867 0 : hDataType = GDT_Byte;
868 0 : nBitDepth = 1;
869 0 : } else if (EQUALN(pszDataType, "2BUI", 4 * sizeof (char))) {
870 0 : hDataType = GDT_Byte;
871 0 : nBitDepth = 2;
872 0 : } else if (EQUALN(pszDataType, "4BUI", 4 * sizeof (char))) {
873 0 : hDataType = GDT_Byte;
874 0 : nBitDepth = 4;
875 0 : } else if (EQUALN(pszDataType, "8BUI", 4 * sizeof (char))) {
876 0 : hDataType = GDT_Byte;
877 0 : nBitDepth = 8;
878 0 : } else if (EQUALN(pszDataType, "8BSI", 4 * sizeof (char))) {
879 0 : hDataType = GDT_Byte;
880 : /**
881 : * To indicate the unsigned byte values between 128 and 255
882 : * should be interpreted as being values between -128 and -1 for
883 : * applications that recognise the SIGNEDBYTE type.
884 : **/
885 0 : bSignedByte = true;
886 0 : nBitDepth = 8;
887 0 : } else if (EQUALN(pszDataType, "16BSI", 5 * sizeof (char))) {
888 0 : hDataType = GDT_Int16;
889 0 : nBitDepth = 16;
890 0 : } else if (EQUALN(pszDataType, "16BUI", 5 * sizeof (char))) {
891 0 : hDataType = GDT_UInt16;
892 0 : nBitDepth = 16;
893 0 : } else if (EQUALN(pszDataType, "32BSI", 5 * sizeof (char))) {
894 0 : hDataType = GDT_Int32;
895 0 : nBitDepth = 32;
896 0 : } else if (EQUALN(pszDataType, "32BUI", 5 * sizeof (char))) {
897 0 : hDataType = GDT_UInt32;
898 0 : nBitDepth = 32;
899 0 : } else if (EQUALN(pszDataType, "32BF", 4 * sizeof (char))) {
900 0 : hDataType = GDT_Float32;
901 0 : nBitDepth = 32;
902 0 : } else if (EQUALN(pszDataType, "64BF", 4 * sizeof (char))) {
903 0 : hDataType = GDT_Float64;
904 0 : nBitDepth = 64;
905 : } else {
906 0 : hDataType = GDT_Byte;
907 0 : nBitDepth = 8;
908 : }
909 :
910 : /* Create raster band object */
911 : SetBand(iBand + 1, new PostGISRasterRasterBand(this, iBand + 1, hDataType,
912 0 : bHasNoDataValue, dfNodata, bSignedByte, nBitDepth, 0, bIsOffline));
913 :
914 0 : CPLFree(pszDataType);
915 0 : PQclear(poResult);
916 : }
917 :
918 0 : return true;
919 : }
920 :
921 : /**
922 : * Read/write a region of image data from multiple bands.
923 : *
924 : * This method allows reading a region of one or more PostGISRasterBands from
925 : * this dataset into a buffer. The write support is still under development
926 : *
927 : * The function fetches all the raster data that intersects with the region
928 : * provided, and store the data in the GDAL cache.
929 : *
930 : * TODO: This only works in case of regular blocking rasters. A more
931 : * general approach to allow non-regular blocking rasters is under development.
932 : *
933 : * It automatically takes care of data type translation if the data type
934 : * (eBufType) of the buffer is different than that of the
935 : * PostGISRasterRasterBand.
936 : *
937 : * TODO: The method should take care of image decimation / replication if the
938 : * buffer size (nBufXSize x nBufYSize) is different than the size of the region
939 : * being accessed (nXSize x nYSize).
940 : *
941 : * The nPixelSpace, nLineSpace and nBandSpace parameters allow reading into or
942 : * writing from various organization of buffers.
943 : *
944 : * @param eRWFlag Either GF_Read to read a region of data, or GF_Write to write
945 : * a region of data.
946 : *
947 : * @param nXOff The pixel offset to the top left corner of the region of the
948 : * band to be accessed. This would be zero to start from the left side.
949 : *
950 : * @param nYOff The line offset to the top left corner of the region of the band
951 : * to be accessed. This would be zero to start from the top.
952 : *
953 : * @param nXSize The width of the region of the band to be accessed in pixels.
954 : *
955 : * @param nYSize The height of the region of the band to be accessed in lines.
956 : *
957 : * @param pData The buffer into which the data should be read, or from which it
958 : * should be written. This buffer must contain at least
959 : * nBufXSize * nBufYSize * nBandCount words of type eBufType. It is organized in
960 : * left to right,top to bottom pixel order. Spacing is controlled by the
961 : * nPixelSpace, and nLineSpace parameters.
962 : *
963 : * @param nBufXSize the width of the buffer image into which the desired region
964 : * is to be read, or from which it is to be written.
965 : *
966 : * @param nBufYSize the height of the buffer image into which the desired region
967 : * is to be read, or from which it is to be written.
968 : *
969 : * @param eBufType the type of the pixel values in the pData data buffer. The
970 : * pixel values will automatically be translated to/from the
971 : * PostGISRasterRasterBand data type as needed.
972 : *
973 : * @param nBandCount the number of bands being read or written.
974 : *
975 : * @param panBandMap the list of nBandCount band numbers being read/written.
976 : * Note band numbers are 1 based. This may be NULL to select the first
977 : * nBandCount bands.
978 : *
979 : * @param nPixelSpace The byte offset from the start of one pixel value in pData
980 : * to the start of the next pixel value within a scanline. If defaulted (0) the
981 : * size of the datatype eBufType is used.
982 : *
983 : * @param nLineSpace The byte offset from the start of one scanline in pData to
984 : * the start of the next. If defaulted (0) the size of the datatype
985 : * eBufType * nBufXSize is used.
986 : *
987 : * @param nBandSpace the byte offset from the start of one bands data to the
988 : * start of the next. If defaulted (0) the value will be nLineSpace * nBufYSize
989 : * implying band sequential organization of the data buffer.
990 : *
991 : * @return CE_Failure if the access fails, otherwise CE_None.
992 : */
993 0 : CPLErr PostGISRasterDataset::IRasterIO(GDALRWFlag eRWFlag,
994 : int nXOff, int nYOff, int nXSize, int nYSize,
995 : void * pData, int nBufXSize, int nBufYSize,
996 : GDALDataType eBufType,
997 : int nBandCount, int *panBandMap,
998 : int nPixelSpace, int nLineSpace, int nBandSpace)
999 : {
1000 : double adfTransform[6];
1001 : double adfProjWin[8];
1002 : int ulx, uly, lrx, lry;
1003 0 : CPLString osCommand;
1004 0 : PGresult* poResult = NULL;
1005 0 : int nTuples = 0;
1006 0 : int iTuplesIndex = 0;
1007 0 : GByte* pbyData = NULL;
1008 0 : int nWKBLength = 0;
1009 : int iBandIndex;
1010 0 : GDALRasterBlock * poBlock = NULL;
1011 : int iBlockXOff, iBlockYOff;
1012 : int nBandDataSize, nBandDataLength;
1013 0 : char * pBandData = NULL;
1014 0 : PostGISRasterRasterBand * poBand = NULL;
1015 0 : GByte * pabySrcBlock = NULL;
1016 : int nBlocksPerRow, nBlocksPerColumn;
1017 : char orderByY[4];
1018 : char orderByX[3];
1019 :
1020 :
1021 : /**
1022 : * TODO: Write support not implemented yet
1023 : **/
1024 0 : if (eRWFlag == GF_Write)
1025 : {
1026 : CPLError(CE_Failure, CPLE_NotSupported,
1027 0 : "PostGIS Raster does not support writing");
1028 0 : return CE_Failure;
1029 : }
1030 :
1031 : /**
1032 : * TODO: Data decimation / replication needed
1033 : */
1034 0 : if (nBufXSize != nXSize || nBufYSize != nYSize)
1035 : {
1036 : /**
1037 : * This will cause individual IReadBlock calls
1038 : *
1039 : */
1040 : return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1041 : pData, nBufXSize, nBufYSize, eBufType, nBandCount,
1042 0 : panBandMap, nPixelSpace, nLineSpace, nBandSpace);
1043 : }
1044 :
1045 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO: "
1046 : "nBandSpace = %d, nLineSpace = %d, nPixelSpace = %d",
1047 0 : nBandSpace, nLineSpace, nPixelSpace);
1048 :
1049 : /**************************************************************************
1050 : * In the first call, we fetch the data from database and store it as
1051 : * 'blocks' in the cache.
1052 : *
1053 : * TODO: If the data is not cached, we must 'invent' a good block size, and
1054 : * divide the data in blocks. To get a proper block size, we should rewrite
1055 : * the GetBlockSize function at band level.
1056 : ***************************************************************************/
1057 0 : if (!bBlocksCached)
1058 : {
1059 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO: "
1060 : "Buffer size = (%d, %d), Region size = (%d, %d)",
1061 0 : nBufXSize, nBufYSize, nXSize, nYSize);
1062 :
1063 : /*******************************************************************
1064 : * Construct a projected window to intersect the band data
1065 : *******************************************************************/
1066 0 : GetGeoTransform(adfTransform);
1067 0 : ulx = nXOff;
1068 0 : uly = nYOff;
1069 0 : lrx = nXOff + nXSize;
1070 0 : lry = nYOff + nYSize;
1071 :
1072 : /* Calculate the intersection polygon */
1073 0 : adfProjWin[0] = adfTransform[0] +
1074 0 : ulx * adfTransform[1] +
1075 0 : uly * adfTransform[2];
1076 :
1077 0 : adfProjWin[1] = adfTransform[3] +
1078 0 : ulx * adfTransform[4] +
1079 0 : uly * adfTransform[5];
1080 :
1081 0 : adfProjWin[2] = adfTransform[0] +
1082 0 : lrx * adfTransform[1] +
1083 0 : uly * adfTransform[2];
1084 :
1085 0 : adfProjWin[3] = adfTransform[3] +
1086 0 : lrx * adfTransform[4] +
1087 0 : uly * adfTransform[5];
1088 :
1089 0 : adfProjWin[4] = adfTransform[0] +
1090 0 : lrx * adfTransform[1] +
1091 0 : lry * adfTransform[2];
1092 :
1093 0 : adfProjWin[5] = adfTransform[3] +
1094 0 : lrx * adfTransform[4] +
1095 0 : lry * adfTransform[5];
1096 :
1097 0 : adfProjWin[6] = adfTransform[0] +
1098 0 : ulx * adfTransform[1] +
1099 0 : lry * adfTransform[2];
1100 :
1101 0 : adfProjWin[7] = adfTransform[3] +
1102 0 : ulx * adfTransform[4] +
1103 0 : lry * adfTransform[5];
1104 :
1105 :
1106 : /* Construct order by for the query */
1107 0 : memset(orderByX, 0, 3);
1108 0 : memset(orderByY, 0, 4);
1109 :
1110 0 : strcpy(orderByX, "asc");
1111 0 : if (nSrid == -1)
1112 0 : strcpy(orderByY, "asc"); // Y starts at 0 and grows
1113 : else
1114 0 : strcpy(orderByY, "desc");// Y starts at max and decreases
1115 :
1116 :
1117 : /*********************************************************************
1118 : * We first get the data from database (ordered from upper left pixel
1119 : * to lower right one)
1120 : *********************************************************************/
1121 0 : if (pszWhere == NULL)
1122 : {
1123 : osCommand.Printf("SELECT %s, ST_ScaleX(%s), ST_SkewY(%s), "
1124 : "ST_SkewX(%s), ST_ScaleY(%s), ST_UpperLeftX(%s), "
1125 : "ST_UpperLeftY(%s), ST_Width(%s), ST_Height(%s) FROM %s.%s WHERE "
1126 : "ST_Intersects(%s, ST_PolygonFromText('POLYGON((%f %f, %f %f, %f %f"
1127 : ", %f %f, %f %f))', %d)) ORDER BY ST_UpperLeftY(%s) %s, "
1128 : "ST_UpperLeftX(%s) %s", pszColumn, pszColumn,
1129 : pszColumn, pszColumn, pszColumn, pszColumn, pszColumn, pszColumn,
1130 : pszColumn, pszSchema, pszTable, pszColumn, adfProjWin[0],
1131 : adfProjWin[1], adfProjWin[2], adfProjWin[3], adfProjWin[4],
1132 : adfProjWin[5], adfProjWin[6], adfProjWin[7], adfProjWin[0],
1133 0 : adfProjWin[1], nSrid, pszColumn, orderByY, pszColumn, orderByX);
1134 : }
1135 :
1136 :
1137 : else
1138 : {
1139 : osCommand.Printf("SELECT %s ST_ScaleX(%s), ST_SkewY(%s), "
1140 : "ST_SkewX(%s), ST_ScaleY(%s), ST_UpperLeftX(%s), "
1141 : "ST_UpperLeftY(%s), ST_Width(%s), ST_Height(%s) FROM %s.%s WHERE %s AND "
1142 : "ST_Intersects(%s, ST_PolygonFromText('POLYGON((%f %f, %f %f, %f %f"
1143 : ", %f %f, %f %f))', %d)) ORDER BY ST_UpperLeftY(%s) %s, "
1144 : "ST_UpperLeftX(%s) %s", pszColumn, pszColumn,
1145 : pszColumn, pszColumn, pszColumn, pszColumn, pszColumn, pszColumn,
1146 : pszColumn,pszSchema, pszTable, pszWhere, pszColumn, adfProjWin[0],
1147 : adfProjWin[1], adfProjWin[2], adfProjWin[3], adfProjWin[4],
1148 : adfProjWin[5], adfProjWin[6], adfProjWin[7], adfProjWin[0],
1149 0 : adfProjWin[1], nSrid, pszColumn, orderByY, pszColumn, orderByX);
1150 : }
1151 :
1152 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): Query = %s",
1153 0 : osCommand.c_str());
1154 :
1155 0 : poResult = PQexec(poConn, osCommand.c_str());
1156 0 : if (poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK ||
1157 : PQntuples(poResult) <= 0)
1158 : {
1159 0 : if (poResult)
1160 0 : PQclear(poResult);
1161 :
1162 : /**
1163 : * This will cause individual IReadBlock calls
1164 : *
1165 : */
1166 : return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1167 : pData, nBufXSize, nBufYSize, eBufType, nBandCount,
1168 0 : panBandMap, nPixelSpace, nLineSpace, nBandSpace);
1169 : }
1170 :
1171 : /**
1172 : * NOTE: In case of any of the raster columns have different SRID, the
1173 : * query will fail. So, if we don't fail, we can assume all the rows
1174 : * have the same SRID. We don't need to check it
1175 : **/
1176 :
1177 :
1178 0 : nTuples = PQntuples(poResult);
1179 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): nTuples = %d",
1180 0 : nTuples);
1181 :
1182 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): "
1183 0 : "Raster size = (%d, %d)", nRasterXSize, nRasterYSize);
1184 :
1185 :
1186 :
1187 : /**************************************************************************
1188 : * This is the simplest case: all the rows have the same dimensions
1189 : * (regularly blocked raster)
1190 : *
1191 : * Now we'll cache each tuple as a data block. More accurately, we must
1192 : * access each tuple, get the band data, and store this data as a block. So,
1193 : * each tuple contains the data for nBands blocks (and nBandCount <=
1194 : * nBands)
1195 : *************************************************************************/
1196 0 : for(iBandIndex = 0; iBandIndex < nBandCount; iBandIndex++)
1197 : {
1198 0 : poBand = (PostGISRasterRasterBand *)GetRasterBand(iBandIndex + 1);
1199 :
1200 0 : nBandDataSize = GDALGetDataTypeSize(poBand->eDataType) / 8;
1201 :
1202 : nBandDataLength = poBand->nBlockXSize * poBand->nBlockYSize *
1203 0 : nBandDataSize;
1204 :
1205 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): "
1206 : "Block size (%d, %d) for band %d", poBand->nBlockXSize,
1207 0 : poBand->nBlockYSize, poBand->nBand);
1208 :
1209 : /* Enables block caching, if it wasn't enabled */
1210 0 : if (!poBand->InitBlockInfo())
1211 0 : continue;
1212 :
1213 : /**
1214 : * This can be different from poBand->nBlocksPerRow and
1215 : * poBand->nBlocksPerColumn, if the region size is different than
1216 : * the raster size. So, we calculate these values for this case.
1217 : */
1218 : nBlocksPerRow =
1219 0 : (nXSize + poBand->nBlockXSize - 1) / poBand->nBlockXSize;
1220 :
1221 : nBlocksPerColumn =
1222 0 : (nYSize + poBand->nBlockYSize - 1) / poBand->nBlockYSize;
1223 :
1224 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): "
1225 0 : "Number of blocks: %dx%d", nBlocksPerRow, nBlocksPerColumn);
1226 :
1227 0 : for(iBlockYOff = 0; iBlockYOff < nBlocksPerColumn;
1228 : iBlockYOff++)
1229 : {
1230 0 : for(iBlockXOff = 0; iBlockXOff < nBlocksPerRow;
1231 : iBlockXOff++)
1232 : {
1233 : iTuplesIndex = (iBlockYOff * nBlocksPerRow) +
1234 0 : iBlockXOff;
1235 :
1236 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): "
1237 : "iBlockXOff = %d, iBlockYOff = %d, "
1238 : "iTuplesIndex = %d", iBlockXOff, iBlockYOff,
1239 0 : iTuplesIndex);
1240 :
1241 : pbyData = CPLHexToBinary(PQgetvalue(poResult, iTuplesIndex,
1242 0 : 1), &nWKBLength);
1243 :
1244 : pBandData = (char *)GET_BAND_DATA(pbyData, poBand->nBand,
1245 0 : nBandDataSize, nBandDataLength);
1246 :
1247 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): "
1248 : "Block data length for band %d: %d", poBand->nBand,
1249 0 : nBandDataLength);
1250 :
1251 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::IRasterIO(): "
1252 0 : "Block (%d, %d)", iBlockXOff, iBlockYOff);
1253 :
1254 : /* Create a new block */
1255 : poBlock = new GDALRasterBlock(poBand, iBlockXOff,
1256 0 : iBlockYOff);
1257 :
1258 0 : poBlock->AddLock();
1259 :
1260 : /* Allocate data space */
1261 0 : if (poBlock->Internalize() != CE_None)
1262 : {
1263 0 : poBlock->DropLock();
1264 0 : delete poBlock;
1265 0 : continue;
1266 : }
1267 :
1268 : /* Add the block to the block matrix */
1269 0 : if (poBand->AdoptBlock(iBlockXOff, iBlockYOff, poBlock) !=
1270 : CE_None)
1271 : {
1272 0 : poBlock->DropLock();
1273 0 : delete poBlock;
1274 0 : continue;
1275 : }
1276 :
1277 : /**
1278 : * Copy data to block
1279 : *
1280 : * TODO: Enable write mode too (mark the block as dirty and
1281 : * create IWriteBlock in PostGISRasterRasterBand)
1282 : */
1283 0 : pabySrcBlock = (GByte *)poBlock->GetDataRef();
1284 :
1285 0 : if (poBand->eDataType == eBufType)
1286 : {
1287 0 : memcpy(pabySrcBlock, pBandData, nBandDataLength);
1288 : }
1289 :
1290 : /**
1291 : * As in GDALDataset class... expensive way of handling
1292 : * single words
1293 : */
1294 : else
1295 : {
1296 : GDALCopyWords(pBandData, poBand->eDataType, 0,
1297 0 : pabySrcBlock, eBufType, 0, 1);
1298 : }
1299 :
1300 0 : poBlock->DropLock();
1301 :
1302 0 : CPLFree(pbyData);
1303 0 : pbyData = NULL;
1304 : }
1305 :
1306 : }
1307 :
1308 : }
1309 :
1310 0 : PQclear(poResult);
1311 0 : bBlocksCached = true;
1312 : }
1313 :
1314 : /* Once the blocks are cached, we delegate in GDAL I/O system */
1315 : return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
1316 : nBufXSize, nBufYSize, eBufType, nBandCount, panBandMap, nPixelSpace,
1317 0 : nLineSpace, nBandSpace);
1318 : }
1319 :
1320 : /******************************************************************************
1321 : * \brief Get the connection information for a filename.
1322 : ******************************************************************************/
1323 : static GBool
1324 2 : GetConnectionInfo(const char * pszFilename,
1325 : char ** ppszConnectionString, char ** ppszSchema, char ** ppszTable,
1326 : char ** ppszColumn, char ** ppszWhere, char ** ppszHost,
1327 : char ** ppszPort, char ** ppszUser, char ** ppszPassword,
1328 : int * nMode, GBool * bBrowseDatabase)
1329 : {
1330 2 : int nPos = -1, i;
1331 2 : char * pszTmp = NULL;
1332 2 : char **papszParams = ParseConnectionString(pszFilename);
1333 2 : if (papszParams == NULL) {
1334 0 : return false;
1335 : }
1336 :
1337 : /**************************************************************************
1338 : * Get mode:
1339 : * - 1. ONE_RASTER_PER_ROW: Each row is considered as a separate raster
1340 : * - 2. ONE_RASTER_PER_TABLE: All the table rows are considered as a whole
1341 : * raster coverage
1342 : **************************************************************************/
1343 2 : nPos = CSLFindName(papszParams, "mode");
1344 2 : if (nPos != -1) {
1345 0 : *nMode = atoi(CPLParseNameValue(papszParams[nPos], NULL));
1346 :
1347 0 : if (*nMode != ONE_RASTER_PER_ROW && *nMode != ONE_RASTER_PER_TABLE) {
1348 : /* Unrecognized mode, using default one */
1349 : /*
1350 : CPLError(CE_Warning, CPLE_AppDefined, "Undefined working mode (%d)."
1351 : " Valid working modes are 1 (ONE_RASTER_PER_ROW) and 2"
1352 : " (ONE_RASTER_PER_TABLE). Using ONE_RASTER_PER_TABLE"
1353 : " by default", nMode);
1354 : */
1355 0 : *nMode = ONE_RASTER_PER_ROW;
1356 : }
1357 :
1358 : /* Remove the mode from connection string */
1359 0 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1360 : }
1361 : /* Default mode */
1362 : else
1363 2 : *nMode = ONE_RASTER_PER_ROW;
1364 :
1365 : /**
1366 : * Case 1: There's no database name: Error, you need, at least,
1367 : * specify a database name (NOTE: insensitive search)
1368 : **/
1369 2 : nPos = CSLFindName(papszParams, "dbname");
1370 2 : if (nPos == -1) {
1371 : CPLError(CE_Failure, CPLE_AppDefined,
1372 0 : "You must specify at least a db name");
1373 :
1374 0 : CSLDestroy(papszParams);
1375 :
1376 0 : return false;
1377 : }
1378 :
1379 : /**
1380 : * Case 2: There's database name, but no table name: activate a flag
1381 : * for browsing the database, fetching all the schemas that contain
1382 : * raster tables
1383 : **/
1384 2 : nPos = CSLFindName(papszParams, "table");
1385 2 : if (nPos == -1) {
1386 0 : *bBrowseDatabase = true;
1387 :
1388 : /* Get schema name, if exist */
1389 0 : nPos = CSLFindName(papszParams, "schema");
1390 0 : if (nPos != -1) {
1391 0 : *ppszSchema = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1392 : /* Delete this pair from params array */
1393 0 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1394 : }
1395 :
1396 : /**
1397 : * Remove the rest of the parameters, if exist (they mustn't be present
1398 : * if we want a valid PQ connection string)
1399 : **/
1400 0 : nPos = CSLFindName(papszParams, "column");
1401 0 : if (nPos != -1) {
1402 : /* Delete this pair from params array */
1403 0 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1404 : }
1405 :
1406 0 : nPos = CSLFindName(papszParams, "where");
1407 0 : if (nPos != -1) {
1408 : /* Delete this pair from params array */
1409 0 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1410 : }
1411 : } else {
1412 2 : *ppszTable = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1413 : /* Delete this pair from params array */
1414 2 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1415 :
1416 : /**
1417 : * Case 3: There's database and table name, but no column
1418 : * name: Use a default column name and use the table to create the
1419 : * dataset
1420 : **/
1421 2 : nPos = CSLFindName(papszParams, "column");
1422 2 : if (nPos == -1) {
1423 2 : *ppszColumn = CPLStrdup(DEFAULT_COLUMN);
1424 : }
1425 : /**
1426 : * Case 4: There's database, table and column name: Use the table to
1427 : * create a dataset
1428 : **/
1429 : else {
1430 0 : *ppszColumn = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1431 : /* Delete this pair from params array */
1432 0 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1433 : }
1434 :
1435 : /* Get the rest of the parameters, if exist */
1436 2 : nPos = CSLFindName(papszParams, "schema");
1437 2 : if (nPos == -1) {
1438 0 : *ppszSchema = CPLStrdup(DEFAULT_SCHEMA);
1439 : } else {
1440 2 : *ppszSchema = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1441 : /* Delete this pair from params array */
1442 2 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1443 : }
1444 :
1445 2 : nPos = CSLFindName(papszParams, "where");
1446 2 : if (nPos != -1) {
1447 0 : *ppszWhere = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1448 : /* Delete this pair from params array */
1449 0 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1450 : }
1451 : }
1452 :
1453 : /* Parse ppszWhere, if needed */
1454 2 : if (*ppszWhere) {
1455 0 : pszTmp = ReplaceQuotes(*ppszWhere, strlen(*ppszWhere));
1456 0 : CPLFree(*ppszWhere);
1457 0 : *ppszWhere = pszTmp;
1458 : }
1459 :
1460 : /***************************************
1461 : * Construct a valid connection string
1462 : ***************************************/
1463 : *ppszConnectionString = (char*) CPLCalloc(strlen(pszFilename),
1464 2 : sizeof (char));
1465 10 : for (i = 0; i < CSLCount(papszParams); i++) {
1466 8 : *ppszConnectionString = strncat(*ppszConnectionString, papszParams[i], strlen(papszParams[i]));
1467 8 : *ppszConnectionString = strncat(*ppszConnectionString, " ", strlen(" "));
1468 : }
1469 :
1470 2 : nPos = CSLFindName(papszParams, "host");
1471 2 : if (nPos != -1) {
1472 2 : *ppszHost = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1473 : }
1474 0 : else if (getenv("PGHOST") != NULL) {
1475 0 : *ppszHost = CPLStrdup(getenv("PGHOST"));
1476 : }
1477 : else {
1478 : CPLError(CE_Failure, CPLE_AppDefined,
1479 : "Host parameter must be provided, or PGHOST environment "
1480 0 : "variable must be set. Please set the host and try again.");
1481 :
1482 0 : CSLDestroy(papszParams);
1483 :
1484 0 : return false;
1485 : }
1486 :
1487 2 : nPos = CSLFindName(papszParams, "port");
1488 2 : if (nPos != -1) {
1489 0 : *ppszPort = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1490 : }
1491 2 : else if (getenv("PGPORT") != NULL ) {
1492 0 : *ppszPort = CPLStrdup(getenv("PGPORT"));
1493 : }
1494 : else {
1495 : CPLError(CE_Failure, CPLE_AppDefined,
1496 : "Port parameter must be provided, or PGPORT environment "
1497 2 : "variable must be set. Please set the port and try again.");
1498 :
1499 2 : CSLDestroy(papszParams);
1500 :
1501 2 : return false;
1502 : }
1503 :
1504 0 : nPos = CSLFindName(papszParams, "user");
1505 0 : if (nPos != -1) {
1506 0 : *ppszUser = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1507 : }
1508 0 : else if (getenv("PGUSER") != NULL ) {
1509 0 : *ppszUser = CPLStrdup(getenv("PGUSER"));
1510 : }
1511 : else {
1512 : CPLError(CE_Failure, CPLE_AppDefined,
1513 : "User parameter must be provided, or PGUSER environment "
1514 0 : "variable must be set. Please set the user and try again.");
1515 :
1516 0 : CSLDestroy(papszParams);
1517 :
1518 0 : return false;
1519 : }
1520 :
1521 0 : nPos = CSLFindName(papszParams, "password");
1522 0 : if (nPos != -1) {
1523 0 : *ppszPassword = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1524 : }
1525 : else {
1526 : // if PGPASSWORD is not set, ppszPassword is set to an empty string.
1527 : // this is okay, since there may be configurations in pg_hba.conf
1528 : // that don't require any password to connect
1529 0 : *ppszPassword = CPLStrdup(getenv("PGPASSWORD"));
1530 : }
1531 :
1532 0 : CSLDestroy(papszParams);
1533 :
1534 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::GetConnectionInfo(): "
1535 : "Mode: %d\nSchema: %s\nTable: %s\nColumn: %s\nWhere: %s\n"
1536 : "Host: %s\nPort: %s\nUser: %s\nPassword: %s\nConnection String: %s",
1537 : *nMode, *ppszSchema, *ppszTable, *ppszColumn,
1538 0 : *ppszWhere, *ppszHost, *ppszPort, *ppszUser, *ppszPassword, *ppszConnectionString);
1539 :
1540 0 : return true;
1541 : }
1542 :
1543 : /******************************************************************************
1544 : * \brief Create a connection to a postgres database
1545 : ******************************************************************************/
1546 : static PGconn *
1547 2 : GetConnection(const char * pszFilename, char ** ppszConnectionString,
1548 : char ** ppszSchema, char ** ppszTable, char ** ppszColumn, char ** ppszWhere,
1549 : int * nMode, GBool * bBrowseDatabase)
1550 : {
1551 : PostGISRasterDriver * poDriver;
1552 2 : PGconn * poConn = NULL;
1553 2 : char * pszHost = NULL;
1554 2 : char * pszPort = NULL;
1555 2 : char * pszUser = NULL;
1556 2 : char * pszPassword = NULL;
1557 :
1558 2 : if (GetConnectionInfo(pszFilename, ppszConnectionString, ppszSchema,
1559 : ppszTable, ppszColumn, ppszWhere, &pszHost, &pszPort, &pszUser,
1560 : &pszPassword, nMode, bBrowseDatabase))
1561 : {
1562 : /********************************************************************
1563 : * Open a new database connection
1564 : ********************************************************************/
1565 0 : poDriver = (PostGISRasterDriver *)GDALGetDriverByName("PostGISRaster");
1566 : poConn = poDriver->GetConnection(*ppszConnectionString,
1567 0 : pszHost, pszPort, pszUser, pszPassword);
1568 :
1569 0 : if (poConn == NULL) {
1570 : CPLError(CE_Failure, CPLE_AppDefined,
1571 0 : "Couldn't establish a database connection");
1572 : }
1573 : }
1574 :
1575 2 : CPLFree(pszHost);
1576 2 : CPLFree(pszPort);
1577 2 : CPLFree(pszUser);
1578 2 : CPLFree(pszPassword);
1579 :
1580 2 : return poConn;
1581 : }
1582 :
1583 :
1584 : /******************************************************************************
1585 : * \brief Open a connection with PostgreSQL. The connection string will have
1586 : * the PostgreSQL accepted format, plus the next key=value pairs:
1587 : * schema = <schema_name>
1588 : * table = <table_name>
1589 : * column = <column_name>
1590 : * where = <SQL where>
1591 : * mode = <working mode> (1 or 2)
1592 : *
1593 : * These pairs are used for selecting the right raster table.
1594 : *****************************************************************************/
1595 22110 : GDALDataset* PostGISRasterDataset::Open(GDALOpenInfo* poOpenInfo) {
1596 22110 : char* pszConnectionString = NULL;
1597 22110 : char* pszSchema = NULL;
1598 22110 : char* pszTable = NULL;
1599 22110 : char* pszColumn = NULL;
1600 22110 : char* pszWhere = NULL;
1601 22110 : int nMode = -1;
1602 22110 : PGconn * poConn = NULL;
1603 22110 : PostGISRasterDataset* poDS = NULL;
1604 22110 : GBool bBrowseDatabase = false;
1605 22110 : PGresult * poResult = NULL;
1606 22110 : CPLString osCommand;
1607 :
1608 : /**************************
1609 : * Check input parameter
1610 : **************************/
1611 22110 : if (poOpenInfo->pszFilename == NULL ||
1612 : poOpenInfo->fp != NULL ||
1613 : !EQUALN(poOpenInfo->pszFilename, "PG:", 3))
1614 : {
1615 : /**
1616 : * Drivers must quietly return NULL if the passed file is not of
1617 : * their format. They should only produce an error if the file
1618 : * does appear to be of their supported format, but for some
1619 : * reason, unsupported or corrupt
1620 : */
1621 22108 : return NULL;
1622 : }
1623 :
1624 : poConn = GetConnection((char *)poOpenInfo->pszFilename,
1625 : &pszConnectionString, &pszSchema, &pszTable, &pszColumn, &pszWhere,
1626 2 : &nMode, &bBrowseDatabase);
1627 2 : if (poConn == NULL) {
1628 2 : CPLFree(pszConnectionString);
1629 2 : CPLFree(pszSchema);
1630 2 : CPLFree(pszTable);
1631 2 : CPLFree(pszColumn);
1632 2 : CPLFree(pszWhere);
1633 2 : return NULL;
1634 : }
1635 :
1636 : /* Check geometry type existence */
1637 0 : poResult = PQexec(poConn, "SELECT oid FROM pg_type WHERE typname = 'geometry'");
1638 0 : if (
1639 : poResult == NULL ||
1640 : PQresultStatus(poResult) != PGRES_TUPLES_OK ||
1641 : PQntuples(poResult) <= 0
1642 : ) {
1643 : CPLError(CE_Failure, CPLE_AppDefined,
1644 : "Error checking geometry type existence. Is PostGIS correctly "
1645 0 : "installed?: %s", PQerrorMessage(poConn));
1646 0 : if (poResult != NULL)
1647 0 : PQclear(poResult);
1648 0 : if (pszSchema)
1649 0 : CPLFree(pszSchema);
1650 0 : if (pszTable)
1651 0 : CPLFree(pszTable);
1652 0 : if (pszColumn)
1653 0 : CPLFree(pszColumn);
1654 0 : if (pszWhere)
1655 0 : CPLFree(pszWhere);
1656 :
1657 0 : return NULL;
1658 : }
1659 :
1660 0 : PQclear(poResult);
1661 :
1662 :
1663 : /* Check spatial tables existence */
1664 : poResult = PQexec(poConn, "select pg_namespace.nspname as schemaname, "
1665 : "pg_class.relname as tablename from pg_class, "
1666 : "pg_namespace where pg_class.relnamespace = pg_namespace.oid "
1667 : "and (pg_class.relname='raster_columns' or "
1668 : "pg_class.relname='raster_overviews' or "
1669 : "pg_class.relname='geometry_columns' or "
1670 0 : "pg_class.relname='spatial_ref_sys')");
1671 0 : if (
1672 : poResult == NULL ||
1673 : PQresultStatus(poResult) != PGRES_TUPLES_OK ||
1674 : PQntuples(poResult) <= 0
1675 : ) {
1676 : CPLError(CE_Failure, CPLE_AppDefined,
1677 : "Error checking needed tables existence: %s",
1678 0 : PQerrorMessage(poConn));
1679 0 : if (poResult != NULL)
1680 0 : PQclear(poResult);
1681 0 : if (pszSchema)
1682 0 : CPLFree(pszSchema);
1683 0 : if (pszTable)
1684 0 : CPLFree(pszTable);
1685 0 : if (pszColumn)
1686 0 : CPLFree(pszColumn);
1687 0 : if (pszWhere)
1688 0 : CPLFree(pszWhere);
1689 :
1690 0 : return NULL;
1691 :
1692 : }
1693 :
1694 0 : PQclear(poResult);
1695 :
1696 : /*************************************************************************
1697 : * No table will be read. Only shows information about the existent raster
1698 : * tables
1699 : *************************************************************************/
1700 0 : if (bBrowseDatabase) {
1701 : /**
1702 : * Creates empty dataset object, only for subdatasets
1703 : **/
1704 0 : poDS = new PostGISRasterDataset();
1705 0 : poDS->poConn = poConn;
1706 0 : poDS->eAccess = GA_ReadOnly;
1707 : //poDS->poDriver = poDriver;
1708 0 : poDS->nMode = (pszSchema) ? BROWSE_SCHEMA : BROWSE_DATABASE;
1709 0 : poDS->nRasterXSize = 0;
1710 0 : poDS->nRasterYSize = 0;
1711 0 : poDS->adfGeoTransform[0] = 0.0;
1712 0 : poDS->adfGeoTransform[1] = 0.0;
1713 0 : poDS->adfGeoTransform[2] = 0.0;
1714 0 : poDS->adfGeoTransform[3] = 0.0;
1715 0 : poDS->adfGeoTransform[4] = 0.0;
1716 0 : poDS->adfGeoTransform[5] = 0.0;
1717 :
1718 : /**
1719 : * Look for raster tables at database and
1720 : * store them as subdatasets
1721 : **/
1722 0 : if (!poDS->BrowseDatabase(pszSchema, pszConnectionString)) {
1723 0 : CPLFree(pszConnectionString);
1724 0 : delete poDS;
1725 :
1726 0 : if (pszSchema)
1727 0 : CPLFree(pszSchema);
1728 0 : if (pszTable)
1729 0 : CPLFree(pszTable);
1730 0 : if (pszColumn)
1731 0 : CPLFree(pszColumn);
1732 0 : if (pszWhere)
1733 0 : CPLFree(pszWhere);
1734 :
1735 0 : return NULL;
1736 : }
1737 :
1738 0 : if (pszSchema)
1739 0 : CPLFree(pszSchema);
1740 0 : if (pszTable)
1741 0 : CPLFree(pszTable);
1742 0 : if (pszColumn)
1743 0 : CPLFree(pszColumn);
1744 0 : if (pszWhere)
1745 0 : CPLFree(pszWhere);
1746 : }
1747 : /***********************************************************************
1748 : * A table will be read: Fetch raster properties from db. Pay attention
1749 : * to the block size: if the raster is blocked at database, the block
1750 : * size can be fetched from each block size, if regular blocking table
1751 : **********************************************************************/
1752 : else {
1753 0 : poDS = new PostGISRasterDataset();
1754 0 : poDS->poConn = poConn;
1755 0 : poDS->eAccess = poOpenInfo->eAccess;
1756 0 : poDS->nMode = nMode;
1757 : //poDS->poDriver = poDriver;
1758 :
1759 0 : poDS->pszSchema = pszSchema;
1760 0 : poDS->pszTable = pszTable;
1761 0 : poDS->pszColumn = pszColumn;
1762 0 : poDS->pszWhere = pszWhere;
1763 :
1764 : /**
1765 : * Fetch basic raster metadata from db
1766 : **/
1767 :
1768 0 : if (!poDS->SetRasterProperties(pszConnectionString)) {
1769 0 : CPLFree(pszConnectionString);
1770 0 : delete poDS;
1771 0 : return NULL;
1772 : }
1773 :
1774 : /* Set raster bands */
1775 0 : if (!poDS->SetRasterBands()) {
1776 0 : CPLFree(pszConnectionString);
1777 0 : delete poDS;
1778 0 : return NULL;
1779 : }
1780 :
1781 : }
1782 :
1783 0 : CPLFree(pszConnectionString);
1784 0 : return poDS;
1785 :
1786 : }
1787 :
1788 : /*****************************************
1789 : * \brief Get Metadata from raster
1790 : * TODO: Add more options (the result of
1791 : * calling ST_Metadata, for example)
1792 : *****************************************/
1793 0 : char** PostGISRasterDataset::GetMetadata(const char *pszDomain) {
1794 0 : if (pszDomain != NULL && EQUALN(pszDomain, "SUBDATASETS", 11))
1795 0 : return papszSubdatasets;
1796 : else
1797 0 : return GDALDataset::GetMetadata(pszDomain);
1798 : }
1799 :
1800 : /*****************************************************
1801 : * \brief Fetch the projection definition string
1802 : * for this dataset in OpenGIS WKT format. It should
1803 : * be suitable for use with the OGRSpatialReference
1804 : * class.
1805 : *****************************************************/
1806 0 : const char* PostGISRasterDataset::GetProjectionRef() {
1807 0 : CPLString osCommand;
1808 : PGresult* poResult;
1809 :
1810 0 : if (nSrid == -1)
1811 0 : return "";
1812 :
1813 0 : if (pszProjection)
1814 0 : return pszProjection;
1815 :
1816 : /********************************************************
1817 : * Reading proj from database
1818 : ********************************************************/
1819 : osCommand.Printf("SELECT srtext FROM spatial_ref_sys where SRID=%d",
1820 0 : nSrid);
1821 0 : poResult = PQexec(this->poConn, osCommand.c_str());
1822 0 : if (poResult && PQresultStatus(poResult) == PGRES_TUPLES_OK
1823 : && PQntuples(poResult) > 0) {
1824 : /*
1825 : * TODO: Memory leak detected with valgrind caused by allocation here.
1826 : * Even when the return string is freed
1827 : */
1828 0 : pszProjection = CPLStrdup(PQgetvalue(poResult, 0, 0));
1829 : }
1830 :
1831 0 : if (poResult)
1832 0 : PQclear(poResult);
1833 :
1834 0 : return pszProjection;
1835 : }
1836 :
1837 : /**********************************************************
1838 : * \brief Set projection definition. The input string must
1839 : * be in OGC WKT or PROJ.4 format
1840 : **********************************************************/
1841 0 : CPLErr PostGISRasterDataset::SetProjection(const char * pszProjectionRef) {
1842 0 : VALIDATE_POINTER1(pszProjectionRef, "SetProjection", CE_Failure);
1843 :
1844 0 : CPLString osCommand;
1845 : PGresult * poResult;
1846 0 : int nFetchedSrid = -1;
1847 :
1848 :
1849 : /*****************************************************************
1850 : * Check if the dataset allows updating
1851 : *****************************************************************/
1852 0 : if (GetAccess() != GA_Update) {
1853 : CPLError(CE_Failure, CPLE_NoWriteAccess,
1854 0 : "This driver doesn't allow write access");
1855 0 : return CE_Failure;
1856 : }
1857 :
1858 : /*****************************************************************
1859 : * Look for projection with this text
1860 : *****************************************************************/
1861 :
1862 : // First, WKT text
1863 : osCommand.Printf("SELECT srid FROM spatial_ref_sys where srtext='%s'",
1864 0 : pszProjectionRef);
1865 0 : poResult = PQexec(poConn, osCommand.c_str());
1866 :
1867 0 : if (poResult && PQresultStatus(poResult) == PGRES_TUPLES_OK
1868 : && PQntuples(poResult) > 0) {
1869 :
1870 0 : nFetchedSrid = atoi(PQgetvalue(poResult, 0, 0));
1871 :
1872 : // update class attribute
1873 0 : nSrid = nFetchedSrid;
1874 :
1875 :
1876 : // update raster_columns table
1877 : osCommand.Printf("UPDATE raster_columns SET srid=%d WHERE \
1878 : r_table_name = '%s' AND r_column = '%s'",
1879 0 : nSrid, pszTable, pszColumn);
1880 0 : poResult = PQexec(poConn, osCommand.c_str());
1881 0 : if (poResult == NULL || PQresultStatus(poResult) != PGRES_COMMAND_OK) {
1882 : CPLError(CE_Failure, CPLE_AppDefined,
1883 : "Couldn't update raster_columns table: %s",
1884 0 : PQerrorMessage(poConn));
1885 0 : return CE_Failure;
1886 : }
1887 :
1888 : // TODO: Update ALL blocks with the new srid...
1889 :
1890 0 : return CE_None;
1891 : }
1892 : // If not, proj4 text
1893 : else {
1894 : osCommand.Printf(
1895 : "SELECT srid FROM spatial_ref_sys where proj4text='%s'",
1896 0 : pszProjectionRef);
1897 0 : poResult = PQexec(poConn, osCommand.c_str());
1898 :
1899 0 : if (poResult && PQresultStatus(poResult) == PGRES_TUPLES_OK
1900 : && PQntuples(poResult) > 0) {
1901 :
1902 0 : nFetchedSrid = atoi(PQgetvalue(poResult, 0, 0));
1903 :
1904 : // update class attribute
1905 0 : nSrid = nFetchedSrid;
1906 :
1907 : // update raster_columns table
1908 : osCommand.Printf("UPDATE raster_columns SET srid=%d WHERE \
1909 : r_table_name = '%s' AND r_column = '%s'",
1910 0 : nSrid, pszTable, pszColumn);
1911 :
1912 0 : poResult = PQexec(poConn, osCommand.c_str());
1913 0 : if (poResult == NULL ||
1914 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
1915 : CPLError(CE_Failure, CPLE_AppDefined,
1916 : "Couldn't update raster_columns table: %s",
1917 0 : PQerrorMessage(poConn));
1918 0 : return CE_Failure;
1919 : }
1920 :
1921 : // TODO: Update ALL blocks with the new srid...
1922 :
1923 0 : return CE_None;
1924 : }
1925 : else {
1926 : CPLError(CE_Failure, CPLE_WrongFormat,
1927 0 : "Couldn't find WKT neither proj4 definition");
1928 0 : return CE_Failure;
1929 : }
1930 0 : }
1931 : }
1932 :
1933 : /********************************************************
1934 : * \brief Set the affine transformation coefficients
1935 : ********************************************************/
1936 0 : CPLErr PostGISRasterDataset::SetGeoTransform(double* padfTransform) {
1937 0 : if (!padfTransform)
1938 0 : return CE_Failure;
1939 :
1940 0 : adfGeoTransform[0] = padfTransform[0];
1941 0 : adfGeoTransform[1] = padfTransform[1];
1942 0 : adfGeoTransform[2] = padfTransform[2];
1943 0 : adfGeoTransform[3] = padfTransform[3];
1944 0 : adfGeoTransform[4] = padfTransform[4];
1945 0 : adfGeoTransform[5] = padfTransform[5];
1946 :
1947 0 : return CE_None;
1948 : }
1949 :
1950 : /********************************************************
1951 : * \brief Get the affine transformation coefficients
1952 : ********************************************************/
1953 0 : CPLErr PostGISRasterDataset::GetGeoTransform(double * padfTransform) {
1954 : // copy necessary values in supplied buffer
1955 0 : padfTransform[0] = adfGeoTransform[0];
1956 0 : padfTransform[1] = adfGeoTransform[1];
1957 0 : padfTransform[2] = adfGeoTransform[2];
1958 0 : padfTransform[3] = adfGeoTransform[3];
1959 0 : padfTransform[4] = adfGeoTransform[4];
1960 0 : padfTransform[5] = adfGeoTransform[5];
1961 :
1962 0 : return CE_None;
1963 : }
1964 :
1965 : /********************************************************
1966 : * \brief Create a copy of a PostGIS Raster dataset.
1967 : ********************************************************/
1968 : GDALDataset *
1969 36 : PostGISRasterDataset::CreateCopy( const char * pszFilename,
1970 : GDALDataset *poGSrcDS, int bStrict, char ** papszOptions,
1971 : GDALProgressFunc pfnProgress, void * pProgressData )
1972 : {
1973 36 : char* pszSchema = NULL;
1974 36 : char* pszTable = NULL;
1975 36 : char* pszColumn = NULL;
1976 36 : char* pszWhere = NULL;
1977 36 : GBool bBrowseDatabase = false;
1978 : int nMode;
1979 36 : char* pszConnectionString = NULL;
1980 : const char* pszSubdatasetName;
1981 36 : PGconn * poConn = NULL;
1982 36 : PGresult * poResult = NULL;
1983 36 : CPLString osCommand;
1984 : GBool bInsertSuccess;
1985 36 : PostGISRasterDataset *poSrcDS = (PostGISRasterDataset *)poGSrcDS;
1986 : PostGISRasterDataset *poSubDS;
1987 :
1988 : // Check connection string
1989 36 : if (pszFilename == NULL ||
1990 : !EQUALN(pszFilename, "PG:", 3)) {
1991 : /**
1992 : * The connection string provided is not a valid connection
1993 : * string.
1994 : */
1995 : CPLError( CE_Failure, CPLE_NotSupported,
1996 : "PostGIS Raster driver was unable to parse the provided "
1997 36 : "connection string." );
1998 36 : return NULL;
1999 : }
2000 :
2001 : poConn = GetConnection(pszFilename, &pszConnectionString, &pszSchema,
2002 0 : &pszTable, &pszColumn, &pszWhere, &nMode, &bBrowseDatabase);
2003 0 : if (poConn == NULL || bBrowseDatabase || pszTable == NULL)
2004 : {
2005 0 : CPLFree(pszConnectionString);
2006 0 : CPLFree(pszSchema);
2007 0 : CPLFree(pszTable);
2008 0 : CPLFree(pszColumn);
2009 0 : CPLFree(pszWhere);
2010 :
2011 : // if connection info fails, browsing mode, or no table set
2012 0 : return NULL;
2013 : }
2014 :
2015 : /* Check geometry type existence */
2016 0 : poResult = PQexec(poConn, "SELECT oid FROM pg_type WHERE typname = 'geometry'");
2017 0 : if (
2018 : poResult == NULL ||
2019 : PQresultStatus(poResult) != PGRES_TUPLES_OK ||
2020 : PQntuples(poResult) <= 0
2021 : ) {
2022 : CPLError(CE_Failure, CPLE_AppDefined,
2023 : "Error checking geometry type existence. Is PostGIS correctly \
2024 0 : installed?: %s", PQerrorMessage(poConn));
2025 0 : if (poResult != NULL)
2026 0 : PQclear(poResult);
2027 0 : if (pszSchema)
2028 0 : CPLFree(pszSchema);
2029 0 : if (pszTable)
2030 0 : CPLFree(pszTable);
2031 0 : if (pszColumn)
2032 0 : CPLFree(pszColumn);
2033 0 : if (pszWhere)
2034 0 : CPLFree(pszWhere);
2035 :
2036 0 : CPLFree(pszConnectionString);
2037 :
2038 0 : return NULL;
2039 : }
2040 :
2041 0 : PQclear(poResult);
2042 :
2043 : /* Check spatial tables existence */
2044 : poResult = PQexec(poConn, "select pg_namespace.nspname as schemaname, \
2045 : pg_class.relname as tablename from pg_class, \
2046 : pg_namespace where pg_class.relnamespace = pg_namespace.oid \
2047 : and (pg_class.relname='raster_columns' or \
2048 : pg_class.relname='raster_overviews' or \
2049 : pg_class.relname='geometry_columns' or \
2050 0 : pg_class.relname='spatial_ref_sys')");
2051 0 : if (
2052 : poResult == NULL ||
2053 : PQresultStatus(poResult) != PGRES_TUPLES_OK ||
2054 : PQntuples(poResult) <= 0
2055 : ) {
2056 : CPLError(CE_Failure, CPLE_AppDefined,
2057 : "Error checking needed tables existence: %s",
2058 0 : PQerrorMessage(poConn));
2059 0 : if (poResult != NULL)
2060 0 : PQclear(poResult);
2061 0 : if (pszSchema)
2062 0 : CPLFree(pszSchema);
2063 0 : if (pszTable)
2064 0 : CPLFree(pszTable);
2065 0 : if (pszColumn)
2066 0 : CPLFree(pszColumn);
2067 0 : if (pszWhere)
2068 0 : CPLFree(pszWhere);
2069 :
2070 0 : CPLFree(pszConnectionString);
2071 :
2072 0 : return NULL;
2073 : }
2074 :
2075 0 : PQclear(poResult);
2076 :
2077 : // begin transaction
2078 0 : poResult = PQexec(poConn, "begin");
2079 0 : if (poResult == NULL ||
2080 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
2081 : CPLError(CE_Failure, CPLE_AppDefined,
2082 : "Error beginning database transaction: %s",
2083 0 : PQerrorMessage(poConn));
2084 0 : if (poResult != NULL)
2085 0 : PQclear(poResult);
2086 0 : if (pszSchema)
2087 0 : CPLFree(pszSchema);
2088 0 : if (pszTable)
2089 0 : CPLFree(pszTable);
2090 0 : if (pszColumn)
2091 0 : CPLFree(pszColumn);
2092 0 : if (pszWhere)
2093 0 : CPLFree(pszWhere);
2094 :
2095 0 : CPLFree(pszConnectionString);
2096 :
2097 0 : return NULL;
2098 : }
2099 :
2100 0 : PQclear(poResult);
2101 :
2102 : // create table for raster (if not exists because a
2103 : // dataset will not be reported for an empty table)
2104 :
2105 : // TODO: is 'rid' necessary?
2106 : osCommand.Printf("create table if not exists %s.%s (rid serial, %s "
2107 : "public.raster, constraint %s_pkey primary key (rid));",
2108 0 : pszSchema, pszTable, pszColumn, pszTable);
2109 0 : poResult = PQexec(poConn, osCommand.c_str());
2110 0 : if (
2111 : poResult == NULL ||
2112 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
2113 :
2114 : CPLError(CE_Failure, CPLE_AppDefined,
2115 : "Error creating needed tables: %s",
2116 0 : PQerrorMessage(poConn));
2117 0 : if (poResult != NULL)
2118 0 : PQclear(poResult);
2119 :
2120 : // rollback
2121 0 : poResult = PQexec(poConn, "rollback");
2122 0 : if (poResult == NULL ||
2123 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
2124 :
2125 : CPLError(CE_Failure, CPLE_AppDefined,
2126 : "Error rolling back transaction: %s",
2127 0 : PQerrorMessage(poConn));
2128 : }
2129 0 : if (poResult != NULL)
2130 0 : PQclear(poResult);
2131 0 : if (pszSchema)
2132 0 : CPLFree(pszSchema);
2133 0 : if (pszTable)
2134 0 : CPLFree(pszTable);
2135 0 : if (pszColumn)
2136 0 : CPLFree(pszColumn);
2137 0 : if (pszWhere)
2138 0 : CPLFree(pszWhere);
2139 :
2140 0 : CPLFree(pszConnectionString);
2141 :
2142 0 : return NULL;
2143 : }
2144 :
2145 0 : PQclear(poResult);
2146 :
2147 : osCommand.Printf("create index %s_%s_gist ON %s.%s USING gist "
2148 : "(public.st_convexhull(%s));", pszTable, pszColumn,
2149 0 : pszSchema, pszTable, pszColumn);
2150 0 : poResult = PQexec(poConn, osCommand.c_str());
2151 0 : if (
2152 : poResult == NULL ||
2153 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
2154 :
2155 : CPLError(CE_Failure, CPLE_AppDefined,
2156 : "Error creating needed index: %s",
2157 0 : PQerrorMessage(poConn));
2158 0 : if (poResult != NULL)
2159 0 : PQclear(poResult);
2160 :
2161 : // rollback
2162 0 : poResult = PQexec(poConn, "rollback");
2163 0 : if (poResult == NULL ||
2164 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
2165 :
2166 : CPLError(CE_Failure, CPLE_AppDefined,
2167 : "Error rolling back transaction: %s",
2168 0 : PQerrorMessage(poConn));
2169 : }
2170 0 : if (poResult != NULL)
2171 0 : PQclear(poResult);
2172 0 : if (pszSchema)
2173 0 : CPLFree(pszSchema);
2174 0 : if (pszTable)
2175 0 : CPLFree(pszTable);
2176 0 : if (pszColumn)
2177 0 : CPLFree(pszColumn);
2178 0 : if (pszWhere)
2179 0 : CPLFree(pszWhere);
2180 :
2181 0 : CPLFree(pszConnectionString);
2182 :
2183 0 : return NULL;
2184 : }
2185 :
2186 0 : PQclear(poResult);
2187 :
2188 0 : if (poSrcDS->nMode == ONE_RASTER_PER_TABLE) {
2189 : // one raster per table
2190 :
2191 : // insert one raster
2192 : bInsertSuccess = InsertRaster(poConn, poSrcDS,
2193 0 : pszSchema, pszTable, pszColumn);
2194 0 : if (!bInsertSuccess) {
2195 : // rollback
2196 0 : poResult = PQexec(poConn, "rollback");
2197 0 : if (poResult == NULL ||
2198 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
2199 :
2200 : CPLError(CE_Failure, CPLE_AppDefined,
2201 : "Error rolling back transaction: %s",
2202 0 : PQerrorMessage(poConn));
2203 : }
2204 0 : if (poResult != NULL)
2205 0 : PQclear(poResult);
2206 0 : if (pszSchema)
2207 0 : CPLFree(pszSchema);
2208 0 : if (pszTable)
2209 0 : CPLFree(pszTable);
2210 0 : if (pszColumn)
2211 0 : CPLFree(pszColumn);
2212 0 : if (pszWhere)
2213 0 : CPLFree(pszWhere);
2214 :
2215 0 : CPLFree(pszConnectionString);
2216 :
2217 0 : return NULL;
2218 : }
2219 : }
2220 0 : else if (poSrcDS->nMode == ONE_RASTER_PER_ROW) {
2221 : // one raster per row
2222 :
2223 : // papszSubdatasets contains name/desc for each subdataset
2224 0 : for (int i = 0; i < CSLCount(poSrcDS->papszSubdatasets); i += 2) {
2225 0 : pszSubdatasetName = CPLParseNameValue( poSrcDS->papszSubdatasets[i], NULL);
2226 0 : if (pszSubdatasetName == NULL) {
2227 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): "
2228 : "Could not parse name/value out of subdataset list: "
2229 0 : "%s", poSrcDS->papszSubdatasets[i]);
2230 0 : continue;
2231 : }
2232 :
2233 : // for each subdataset
2234 0 : GDALOpenInfo poOpenInfo( pszSubdatasetName, GA_ReadOnly );
2235 : // open the subdataset
2236 0 : poSubDS = (PostGISRasterDataset *)Open(&poOpenInfo);
2237 :
2238 0 : if (poSubDS == NULL) {
2239 : // notify!
2240 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): "
2241 : "Could not open a subdataset: %s",
2242 0 : pszSubdatasetName);
2243 0 : continue;
2244 : }
2245 :
2246 : // insert one raster
2247 : bInsertSuccess = InsertRaster(poConn, poSubDS,
2248 0 : pszSchema, pszTable, pszColumn);
2249 :
2250 0 : if (!bInsertSuccess) {
2251 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): "
2252 0 : "Could not copy raster subdataset to new dataset." );
2253 :
2254 : // keep trying ...
2255 : }
2256 :
2257 : // close this dataset
2258 0 : GDALClose((GDALDatasetH)poSubDS);
2259 : }
2260 : }
2261 :
2262 : // commit transaction
2263 0 : poResult = PQexec(poConn, "commit");
2264 0 : if (poResult == NULL ||
2265 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
2266 : CPLError(CE_Failure, CPLE_AppDefined,
2267 : "Error committing database transaction: %s",
2268 0 : PQerrorMessage(poConn));
2269 0 : if (poResult != NULL)
2270 0 : PQclear(poResult);
2271 0 : if (pszSchema)
2272 0 : CPLFree(pszSchema);
2273 0 : if (pszTable)
2274 0 : CPLFree(pszTable);
2275 0 : if (pszColumn)
2276 0 : CPLFree(pszColumn);
2277 0 : if (pszWhere)
2278 0 : CPLFree(pszWhere);
2279 :
2280 0 : CPLFree(pszConnectionString);
2281 :
2282 0 : return NULL;
2283 : }
2284 :
2285 0 : PQclear(poResult);
2286 :
2287 0 : if (pszSchema)
2288 0 : CPLFree(pszSchema);
2289 0 : if (pszTable)
2290 0 : CPLFree(pszTable);
2291 0 : if (pszColumn)
2292 0 : CPLFree(pszColumn);
2293 0 : if (pszWhere)
2294 0 : CPLFree(pszWhere);
2295 :
2296 0 : CPLFree(pszConnectionString);
2297 :
2298 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): "
2299 0 : "Opening new dataset: %s", pszFilename);
2300 :
2301 : // connect to the new dataset
2302 0 : GDALOpenInfo poOpenInfo( pszFilename, GA_Update );
2303 : // open the newdataset
2304 0 : poSubDS = (PostGISRasterDataset *)Open(&poOpenInfo);
2305 :
2306 0 : if (poSubDS == NULL) {
2307 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): "
2308 0 : "New dataset could not be opened.");
2309 : }
2310 :
2311 0 : return poSubDS;
2312 : }
2313 :
2314 : /********************************************************
2315 : * \brief Helper method to insert a new raster.
2316 : ********************************************************/
2317 : GBool
2318 0 : PostGISRasterDataset::InsertRaster(PGconn * poConn,
2319 : PostGISRasterDataset * poSrcDS, const char *pszSchema,
2320 : const char * pszTable, const char * pszColumn)
2321 : {
2322 0 : CPLString osCommand;
2323 0 : PGresult * poResult = NULL;
2324 :
2325 0 : if (poSrcDS->pszWhere == NULL) {
2326 : osCommand.Printf("insert into %s.%s (%s) (select %s from %s.%s)",
2327 : pszSchema, pszTable, pszColumn, poSrcDS->pszColumn,
2328 0 : poSrcDS->pszSchema, poSrcDS->pszTable);
2329 : }
2330 : else {
2331 : osCommand.Printf("insert into %s.%s (%s) (select %s from %s.%s where %s)",
2332 : pszSchema, pszTable, pszColumn, poSrcDS->pszColumn,
2333 0 : poSrcDS->pszSchema, poSrcDS->pszTable, poSrcDS->pszWhere);
2334 : }
2335 :
2336 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::InsertRaster(): Query = %s",
2337 0 : osCommand.c_str());
2338 :
2339 0 : poResult = PQexec(poConn, osCommand.c_str());
2340 0 : if (
2341 : poResult == NULL ||
2342 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
2343 :
2344 : CPLError(CE_Failure, CPLE_AppDefined,
2345 : "Error inserting raster: %s",
2346 0 : PQerrorMessage(poConn));
2347 0 : if (poResult != NULL)
2348 0 : PQclear(poResult);
2349 :
2350 0 : return false;
2351 : }
2352 :
2353 0 : PQclear(poResult);
2354 :
2355 0 : return true;
2356 : }
2357 :
2358 : /*********************************************************
2359 : * \brief Delete a PostGIS Raster dataset.
2360 : *********************************************************/
2361 : CPLErr
2362 0 : PostGISRasterDataset::Delete(const char* pszFilename)
2363 : {
2364 0 : char* pszSchema = NULL;
2365 0 : char* pszTable = NULL;
2366 0 : char* pszColumn = NULL;
2367 0 : char* pszWhere = NULL;
2368 : GBool bBrowseDatabase;
2369 0 : char* pszConnectionString = NULL;
2370 : int nMode;
2371 0 : PGconn * poConn = NULL;
2372 0 : PGresult * poResult = NULL;
2373 0 : CPLString osCommand;
2374 0 : CPLErr nError = CE_Failure;
2375 :
2376 : // Check connection string
2377 0 : if (pszFilename == NULL ||
2378 : !EQUALN(pszFilename, "PG:", 3)) {
2379 : /**
2380 : * The connection string provided is not a valid connection
2381 : * string.
2382 : */
2383 : CPLError( CE_Failure, CPLE_NotSupported,
2384 : "PostGIS Raster driver was unable to parse the provided "
2385 0 : "connection string. Nothing was deleted." );
2386 0 : return CE_Failure;
2387 : }
2388 :
2389 : poConn = GetConnection(pszFilename, &pszConnectionString,
2390 : &pszSchema, &pszTable, &pszColumn, &pszWhere,
2391 0 : &nMode, &bBrowseDatabase);
2392 0 : if (poConn == NULL) {
2393 0 : CPLFree(pszConnectionString);
2394 0 : CPLFree(pszSchema);
2395 0 : CPLFree(pszTable);
2396 0 : CPLFree(pszColumn);
2397 0 : CPLFree(pszWhere);
2398 :
2399 0 : return CE_Failure;
2400 : }
2401 :
2402 : // begin transaction
2403 0 : poResult = PQexec(poConn, "begin");
2404 0 : if (poResult == NULL ||
2405 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
2406 : CPLError(CE_Failure, CPLE_AppDefined,
2407 : "Error beginning database transaction: %s",
2408 0 : PQerrorMessage(poConn));
2409 :
2410 : // set nMode to NO_MODE to avoid any further processing
2411 0 : nMode = NO_MODE;
2412 : }
2413 :
2414 0 : PQclear(poResult);
2415 :
2416 0 : if ( nMode == ONE_RASTER_PER_TABLE ||
2417 : (nMode == ONE_RASTER_PER_ROW && pszWhere == NULL)) {
2418 : // without a where clause, this delete command shall delete
2419 : // all subdatasets, even if the mode is ONE_RASTER_PER_ROW
2420 :
2421 : // drop table <schema>.<table>;
2422 0 : osCommand.Printf("drop table %s.%s", pszSchema, pszTable);
2423 0 : poResult = PQexec(poConn, osCommand.c_str());
2424 0 : if (poResult == NULL ||
2425 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
2426 : CPLError(CE_Failure, CPLE_AppDefined,
2427 : "Couldn't drop the table %s.%s: %s",
2428 0 : pszSchema, pszTable, PQerrorMessage(poConn));
2429 : }
2430 : else {
2431 0 : PQclear(poResult);
2432 0 : nError = CE_None;
2433 : }
2434 : }
2435 0 : else if (nMode == ONE_RASTER_PER_ROW) {
2436 :
2437 : // delete from <schema>.<table> where <where>
2438 : osCommand.Printf("delete from %s.%s where %s", pszSchema,
2439 0 : pszTable, pszWhere);
2440 0 : poResult = PQexec(poConn, osCommand.c_str());
2441 0 : if (poResult == NULL ||
2442 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
2443 : CPLError(CE_Failure, CPLE_AppDefined,
2444 : "Couldn't delete records from the table %s.%s: %s",
2445 0 : pszSchema, pszTable, PQerrorMessage(poConn));
2446 : }
2447 : else {
2448 0 : PQclear(poResult);
2449 0 : nError = CE_None;
2450 : }
2451 : }
2452 :
2453 : // if mode == NO_MODE, the begin transaction above did not complete,
2454 : // so no commit is necessary
2455 0 : if (nMode != NO_MODE) {
2456 0 : poResult = PQexec(poConn, "commit");
2457 0 : if (poResult == NULL ||
2458 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
2459 : CPLError(CE_Failure, CPLE_AppDefined,
2460 : "Error committing database transaction: %s",
2461 0 : PQerrorMessage(poConn));
2462 :
2463 0 : nError = CE_Failure;
2464 : }
2465 : }
2466 :
2467 0 : if (poResult)
2468 0 : PQclear(poResult);
2469 0 : if (pszSchema)
2470 0 : CPLFree(pszSchema);
2471 0 : if (pszTable)
2472 0 : CPLFree(pszTable);
2473 0 : if (pszColumn)
2474 0 : CPLFree(pszColumn);
2475 0 : if (pszWhere)
2476 0 : CPLFree(pszWhere);
2477 :
2478 : // clean up connection string
2479 0 : CPLFree(pszConnectionString);
2480 :
2481 0 : return nError;
2482 : }
2483 :
2484 : /************************************************************************/
2485 : /* GDALRegister_PostGISRaster() */
2486 :
2487 : /************************************************************************/
2488 1135 : void GDALRegister_PostGISRaster() {
2489 : GDALDriver *poDriver;
2490 :
2491 1135 : if (GDALGetDriverByName("PostGISRaster") == NULL) {
2492 1093 : poDriver = new PostGISRasterDriver();
2493 :
2494 1093 : poDriver->SetDescription("PostGISRaster");
2495 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
2496 1093 : "PostGIS Raster driver");
2497 :
2498 1093 : poDriver->pfnOpen = PostGISRasterDataset::Open;
2499 1093 : poDriver->pfnCreateCopy = PostGISRasterDataset::CreateCopy;
2500 1093 : poDriver->pfnDelete = PostGISRasterDataset::Delete;
2501 :
2502 1093 : GetGDALDriverManager()->RegisterDriver(poDriver);
2503 : }
2504 1135 : }
2505 :
|