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 "gdal.h"
36 : #include "cpl_conv.h"
37 : #include "cpl_string.h"
38 : #include "gdal_priv.h"
39 : #include <math.h>
40 : #include "cpl_error.h"
41 : #include "ogr_core.h"
42 :
43 : #ifdef OGR_ENABLED
44 : #include "ogr_api.h"
45 : #endif
46 :
47 : #include "ogr_geometry.h"
48 : #include "gdal_vrt.h"
49 : #include "vrtdataset.h"
50 : #include "memdataset.h"
51 :
52 : #ifdef _WIN32
53 : #define rint(x) floor((x) + 0.5)
54 : #endif
55 :
56 :
57 : CPL_C_START
58 : void GDALRegister_PostGISRaster(void);
59 : CPL_C_END
60 :
61 :
62 :
63 : /************************
64 : * \brief Constructor
65 : ************************/
66 0 : PostGISRasterDataset::PostGISRasterDataset(ResolutionStrategy inResolutionStrategy) {
67 0 : pszOriginalConnectionString = NULL;
68 0 : papszSubdatasets = NULL;
69 0 : nSrid = -1;
70 0 : poConn = NULL;
71 0 : bRegisteredInRasterColumns = false;
72 0 : pszSchema = NULL;
73 0 : pszTable = NULL;
74 0 : pszColumn = NULL;
75 0 : pszWhere = NULL;
76 0 : pszProjection = NULL;
77 0 : resolutionStrategy = inResolutionStrategy;
78 0 : nTiles = 0;
79 0 : nMode = NO_MODE;
80 0 : poDriver = NULL;
81 0 : adfGeoTransform[GEOTRSFRM_TOPLEFT_X] = 0.0;
82 0 : adfGeoTransform[GEOTRSFRM_WE_RES] = 0.0;
83 0 : adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] = 0.0;
84 0 : adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] = 0.0;
85 0 : adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] = 0.0;
86 0 : adfGeoTransform[GEOTRSFRM_NS_RES] = 0.0;
87 0 : bBlocksCached = false;
88 0 : bRegularBlocking = true;// do not change! (need to be 'true' for SetRasterProperties)
89 0 : bAllTilesSnapToSameGrid = false;
90 :
91 : /**
92 : * TODO: Parametrize bAllTilesSnapToSameGrid. It controls if all the
93 : * raster rows, in ONE_RASTER_PER_TABLE mode, must be checked to test if
94 : * they snap to the same grid and have the same srid. It can be the user
95 : * decission, if he/she's sure all the rows pass the test and want more
96 : * speed.
97 : **/
98 :
99 0 : }
100 :
101 : /************************
102 : * \brief Constructor
103 : ************************/
104 0 : PostGISRasterDataset::~PostGISRasterDataset() {
105 :
106 0 : if (pszSchema)
107 0 : CPLFree(pszSchema);
108 0 : if (pszTable)
109 0 : CPLFree(pszTable);
110 0 : if (pszColumn)
111 0 : CPLFree(pszColumn);
112 0 : if (pszWhere)
113 0 : CPLFree(pszWhere);
114 0 : if (pszProjection)
115 0 : CPLFree(pszProjection);
116 0 : if (pszOriginalConnectionString)
117 0 : CPLFree(pszOriginalConnectionString);
118 :
119 0 : if (papszSubdatasets)
120 0 : CSLDestroy(papszSubdatasets);
121 0 : }
122 :
123 : /**************************************************************
124 : * \brief Replace the single quotes by " in the input string
125 : *
126 : * Needed before tokenize function
127 : *************************************************************/
128 : static
129 1 : char * ReplaceSingleQuotes(const char * pszInput, int nLength) {
130 : int i;
131 1 : char* pszOutput = NULL;
132 :
133 1 : if (nLength == -1)
134 1 : nLength = strlen(pszInput);
135 :
136 1 : pszOutput = (char*) CPLCalloc(nLength + 1, sizeof (char));
137 :
138 93 : for (i = 0; i < nLength; i++) {
139 92 : if (pszInput[i] == '\'')
140 12 : pszOutput[i] = '"';
141 : else
142 80 : pszOutput[i] = pszInput[i];
143 :
144 : }
145 :
146 1 : return pszOutput;
147 : }
148 :
149 : /**************************************************************
150 : * \brief Replace the quotes by single quotes in the input string
151 : *
152 : * Needed in the 'where' part of the input string
153 : *************************************************************/
154 : static
155 0 : char * ReplaceQuotes(const char * pszInput, int nLength) {
156 : int i;
157 0 : char * pszOutput = NULL;
158 :
159 0 : if (nLength == -1)
160 0 : nLength = strlen(pszInput);
161 :
162 0 : pszOutput = (char*) CPLCalloc(nLength + 1, sizeof (char));
163 :
164 0 : for (i = 0; i < nLength; i++) {
165 0 : if (pszInput[i] == '"')
166 0 : pszOutput[i] = '\'';
167 : else
168 0 : pszOutput[i] = pszInput[i];
169 : }
170 :
171 0 : return pszOutput;
172 : }
173 :
174 : /*****************************************************************************
175 : * \brief Split connection string into user, password, host, database...
176 : *
177 : * The parameters separated by spaces are return as a list of strings. The
178 : * function accepts all the PostgreSQL recognized parameter key words.
179 : *
180 : * The returned list must be freed with CSLDestroy when no longer needed
181 : *
182 : *****************************************************************************/
183 : static
184 1 : char** ParseConnectionString(const char * pszConnectionString) {
185 1 : char * pszEscapedConnectionString = NULL;
186 :
187 : /* Escape string following SQL scheme */
188 1 : pszEscapedConnectionString = ReplaceSingleQuotes(pszConnectionString, -1);
189 :
190 : /* Avoid PG: part */
191 1 : char* pszStartPos = (char*) strstr(pszEscapedConnectionString, ":") + 1;
192 :
193 : /* Tokenize */
194 : char** papszParams = CSLTokenizeString2(pszStartPos, " ",
195 1 : CSLT_HONOURSTRINGS);
196 :
197 : /* Free */
198 1 : CPLFree(pszEscapedConnectionString);
199 :
200 1 : return papszParams;
201 :
202 : }
203 :
204 : /**************************************************************************
205 : * \brief Look for raster tables in database and store them as subdatasets
206 : *
207 : * If no table is provided in connection string, the driver looks for the
208 : * existent raster tables in the schema given as argument. This argument,
209 : * however, is optional. If a NULL value is provided, the driver looks for
210 : * all raster tables in all schemas of the user-provided database.
211 : *
212 : * NOTE: Permissions are managed by libpq. The driver only returns an error
213 : * if an error is returned when trying to access to tables not allowed to
214 : * the current user.
215 : **************************************************************************/
216 0 : GBool PostGISRasterDataset::BrowseDatabase(const char* pszCurrentSchema,
217 : char* pszValidConnectionString) {
218 : /* Be careful! These 3 vars override the class ones! */
219 0 : char* pszSchema = NULL;
220 0 : char* pszTable = NULL;
221 0 : char* pszColumn = NULL;
222 0 : int i = 0;
223 0 : int nTuples = 0;
224 0 : PGresult * poResult = NULL;
225 0 : CPLString osCommand;
226 :
227 :
228 : /*************************************************************
229 : * Fetch all the raster tables and store them as subdatasets
230 : *************************************************************/
231 0 : if (pszCurrentSchema == NULL) {
232 : osCommand.Printf("select pg_namespace.nspname as schema, pg_class.relname as \
233 : table, pg_attribute.attname as column from pg_class, \
234 : pg_namespace,pg_attribute, pg_type where \
235 : pg_class.relnamespace = pg_namespace.oid and pg_class.oid = \
236 : pg_attribute.attrelid and pg_attribute.atttypid = pg_type.oid \
237 0 : and pg_type.typname = 'raster'");
238 :
239 0 : poResult = PQexec(poConn, osCommand.c_str());
240 0 : if (
241 : poResult == NULL ||
242 : PQresultStatus(poResult) != PGRES_TUPLES_OK ||
243 : PQntuples(poResult) <= 0
244 : ) {
245 : CPLError(CE_Failure, CPLE_AppDefined,
246 0 : "Error browsing database for PostGIS Raster tables: %s", PQerrorMessage(poConn));
247 0 : if (poResult != NULL)
248 0 : PQclear(poResult);
249 :
250 0 : return false;
251 : }
252 :
253 :
254 0 : nTuples = PQntuples(poResult);
255 0 : for (i = 0; i < nTuples; i++) {
256 0 : pszSchema = PQgetvalue(poResult, i, 0);
257 0 : pszTable = PQgetvalue(poResult, i, 1);
258 0 : pszColumn = PQgetvalue(poResult, i, 2);
259 :
260 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
261 : CPLSPrintf("SUBDATASET_%d_NAME", (i + 1)),
262 : CPLSPrintf("PG:%s schema=%s table=%s column=%s",
263 0 : pszValidConnectionString, pszSchema, pszTable, pszColumn));
264 :
265 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
266 : CPLSPrintf("SUBDATASET_%d_DESC", (i + 1)),
267 0 : CPLSPrintf("PostGIS Raster table at %s.%s (%s)", pszSchema, pszTable, pszColumn));
268 : }
269 :
270 0 : PQclear(poResult);
271 :
272 : }
273 : /**********************************************************************
274 : * Fetch all the schema's raster tables and store them as subdatasets
275 : **********************************************************************/
276 : else {
277 : osCommand.Printf("select pg_class.relname as table, pg_attribute.attname \
278 : as column from pg_class, pg_namespace,pg_attribute, pg_type where \
279 : pg_class.relnamespace = pg_namespace.oid and pg_class.oid = \
280 : pg_attribute.attrelid and pg_attribute.atttypid = pg_type.oid \
281 : and pg_type.typname = 'raster' and pg_namespace.nspname = '%s'",
282 0 : pszCurrentSchema);
283 :
284 0 : poResult = PQexec(poConn, osCommand.c_str());
285 0 : if (
286 : poResult == NULL ||
287 : PQresultStatus(poResult) != PGRES_TUPLES_OK ||
288 : PQntuples(poResult) <= 0
289 : ) {
290 : CPLError(CE_Failure, CPLE_AppDefined,
291 0 : "Error browsing database for PostGIS Raster tables: %s", PQerrorMessage(poConn));
292 0 : if (poResult != NULL)
293 0 : PQclear(poResult);
294 :
295 0 : return false;
296 : }
297 :
298 :
299 0 : nTuples = PQntuples(poResult);
300 0 : for (i = 0; i < nTuples; i++) {
301 0 : pszTable = PQgetvalue(poResult, i, 0);
302 0 : pszColumn = PQgetvalue(poResult, i, 1);
303 :
304 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
305 : CPLSPrintf("SUBDATASET_%d_NAME", (i + 1)),
306 : CPLSPrintf("PG:%s schema=%s table=%s column=%s",
307 0 : pszValidConnectionString, pszCurrentSchema, pszTable, pszColumn));
308 :
309 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
310 : CPLSPrintf("SUBDATASET_%d_DESC", (i + 1)),
311 : CPLSPrintf("PostGIS Raster table at %s.%s (%s)", pszCurrentSchema,
312 0 : pszTable, pszColumn));
313 : }
314 :
315 0 : PQclear(poResult);
316 : }
317 :
318 0 : return true;
319 : }
320 :
321 :
322 :
323 :
324 : /*************************************************************************
325 : * \brief Set the general raster properties.
326 : *
327 : * We must distinguish between tiled and untiled raster coverages. In
328 : * PostGIS Raster, there's no real difference between 'tile' and 'raster'.
329 : * There's only 'raster objects'. Each record of a raster table is a
330 : * raster object, and has its own georeference information, whether if
331 : * the record is a tile of a bigger raster coverage or is a complete
332 : * raster. So, <b>there's no a way of knowing if the rows of a raster
333 : * table are related or not</b>. It's user's responsibility. The only
334 : * thing driver can do is to suppose all the rows of a table are from
335 : * the same raster coverage if the user has queried for one table, without
336 : * specifying a where clause.
337 : *
338 : * The user is responsible to ensure that the raster layer meets the minimum
339 : * topological requirements for analysis. The ideal case is when all the raster
340 : * tiles of a continuous layer are the same size, snap to the same grid and do
341 : * not overlap.
342 : *
343 : * So, when we query for a raster table, we have 2 different cases:
344 : * - The result is only one row OR there are several rows BUT the working
345 : * mode is ONE_RASTER_PER_TABLE. The row(s) returned form one raster coverage.
346 : * We get the whole coverage extent (except rotated rasters), and its georefence,
347 : * if possible
348 : * - The result are several rows of a table AND the working mode is
349 : * ONE_RASTER_PER_ROW. We assume each row is a different raster object,
350 : * and is reported as a subdataset.
351 : **************************************************************************/
352 0 : GBool PostGISRasterDataset::SetRasterProperties
353 : (const char * pszValidConnectionString)
354 : {
355 0 : PGresult* poResult = NULL;
356 0 : CPLString osCommand;
357 0 : int i = 0;
358 0 : int nTuples = 0;
359 0 : int nRasterID = 0;
360 0 : char* pszIdColumn = NULL;
361 : double tileUpperLeftX;
362 : double tileUpperLeftY;
363 : double tileSkewX;
364 : double tileSkewY;
365 : double tilePixelSizeX;
366 : double tilePixelSizeY;
367 0 : int nTileWidth = 0;
368 0 : int nTileHeight = 0;
369 0 : int nPreviousTileWidth = 0;
370 0 : int nPreviousTileHeight = 0;
371 0 : int nBlockXSize = 0, nBlockYSize = 0;
372 : char szTmp[20];
373 :
374 : /* Incorporated variables from old SetRasterBand method */
375 0 : GBool bSignedByte = false;
376 0 : int nBitDepth = 8;
377 0 : char* pszDataType = NULL;
378 0 : int iBand = 0;
379 0 : double dfNodata = 0.0;
380 0 : GDALDataType hDataType = GDT_Byte;
381 0 : GBool bIsOffline = false;
382 0 : GBool bHasNoDataValue = false;
383 :
384 : /**************************************************************************
385 : * Get the extent and the maximum number of bands of the requested raster
386 : * TODO: The extent of rotated rasters could be a problem. We'll need a
387 : * ST_RotatedExtent function in PostGIS. Without that function, we shouldn't
388 : * allow rotated rasters
389 : **************************************************************************/
390 : // NOTE: can't use 'srid' alias in the GROUP BY. It doesn't work
391 : // with PostgreSQL 9.1
392 0 : if (pszWhere == NULL) {
393 : osCommand.Printf(
394 : "select srid, nbband, st_xmin(geom) as xmin, st_xmax(geom) as xmax, "
395 : "st_ymin(geom) as ymin, st_ymax(geom) as ymax from (select st_srid(%s) srid, "
396 : "st_extent(%s::geometry) geom, max(ST_NumBands(rast)) nbband from %s.%s "
397 : "group by st_srid(%s)) foo", pszColumn, pszColumn, pszSchema,
398 0 : pszTable, pszColumn);
399 : }
400 :
401 : else {
402 : osCommand.Printf(
403 : "select srid, nbband, st_xmin(geom) as xmin, st_xmax(geom) as xmax, "
404 : "st_ymin(geom) as ymin, st_ymax(geom) as ymax from (select st_srid(%s) srid, "
405 : "st_extent(%s::geometry) geom, max(ST_NumBands(rast)) nbband from %s.%s "
406 : "where %s group by st_srid(%s)) foo", pszColumn, pszColumn, pszSchema,
407 0 : pszTable, pszWhere, pszColumn);
408 : }
409 :
410 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
411 0 : "First query: %s", osCommand.c_str());
412 :
413 :
414 0 : poResult = PQexec(poConn, osCommand.c_str());
415 :
416 : // Query execution error
417 0 : if(poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK ||
418 : PQntuples(poResult) < 0) {
419 :
420 : CPLError(CE_Failure, CPLE_AppDefined, "Error browsing database for "
421 0 : "PostGIS Raster properties");
422 :
423 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
424 0 : "%s", PQerrorMessage(poConn));
425 :
426 0 : if (poResult != NULL)
427 0 : PQclear(poResult);
428 :
429 0 : return false;
430 : }
431 :
432 : // No tuples
433 0 : else if (PQntuples(poResult) == 0) {
434 : CPLError(CE_Failure, CPLE_AppDefined, "Error, no data found in the table that "
435 : "matches your constraints. Maybe there are no rows in the table, or maybe "
436 0 : "you provide a 'where' option with too much restrictions");
437 :
438 0 : PQclear(poResult);
439 :
440 0 : return false;
441 : }
442 :
443 :
444 : /**
445 : * TODO: We could provide an extra parameter, to transform all the tiles to
446 : * the same SRID
447 : **/
448 0 : else if (PQntuples(poResult) > 1) {
449 : CPLError(CE_Failure, CPLE_AppDefined,
450 : "Error, the table %s.%s contains tiles with different srid. This feature "
451 : "is not yet supported by the PostGIS Raster driver. Please, specify a table "
452 : "that contains only tiles with the same srid or provide a 'where' constraint "
453 0 : "to select just the tiles with the same value for srid", pszSchema, pszTable);
454 :
455 0 : PQclear(poResult);
456 0 : return false;
457 : }
458 :
459 : // Get some information we will probably need further
460 0 : nSrid = atoi(PQgetvalue(poResult, 0, 0));
461 0 : nBands = atoi(PQgetvalue(poResult, 0, 1));
462 0 : xmin = atof(PQgetvalue(poResult, 0, 2));
463 0 : xmax = atof(PQgetvalue(poResult, 0, 3));
464 0 : ymin = atof(PQgetvalue(poResult, 0, 4));
465 0 : ymax = atof(PQgetvalue(poResult, 0, 5));
466 :
467 0 : PQclear(poResult);
468 :
469 : /*****************************************************************************
470 : * Now, we're going to count the number of raster tiles we will have to deal
471 : * with.To save one database server round, we get the pixel size and rotation
472 : *
473 : * TODO: Improve the optimization, based on MAX_TILES
474 : *****************************************************************************/
475 0 : memset(szTmp, 0, sizeof(szTmp));
476 : if (MAX_TILES > 0) {
477 0 : sprintf(szTmp, "limit %d", MAX_TILES);
478 : }
479 :
480 :
481 0 : if (pszWhere == NULL) {
482 : osCommand.Printf(
483 : "select st_scalex(%s), st_scaley(%s), st_skewx(%s), "
484 : "st_skewy(%s), st_width(%s), st_height(%s) from %s.%s %s", pszColumn, pszColumn,
485 0 : pszColumn, pszColumn, pszColumn, pszColumn, pszSchema, pszTable, szTmp);
486 : }
487 :
488 : else {
489 : osCommand.Printf(
490 : "select st_scalex(%s), st_scaley(%s), st_skewx(%s), "
491 : "st_skewy(%s), st_width(%s), st_height(%s) from %s.%s where %s %s", pszColumn,
492 : pszColumn, pszColumn, pszColumn, pszColumn, pszColumn, pszSchema, pszTable,
493 0 : pszWhere, szTmp);
494 : }
495 :
496 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
497 0 : "Query: %s", osCommand.c_str());
498 :
499 0 : poResult = PQexec(poConn, osCommand.c_str());
500 0 : if (poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK ||
501 : PQntuples(poResult) <= 0) {
502 :
503 :
504 0 : CPLError(CE_Failure, CPLE_AppDefined, "Error retrieving raster metadata");
505 :
506 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
507 0 : "%s", PQerrorMessage(poConn));
508 :
509 0 : if (poResult != NULL)
510 0 : PQclear(poResult);
511 :
512 0 : return false;
513 : }
514 :
515 : // Now we now the number of tiles that form our dataset
516 0 : nTiles = PQntuples(poResult);
517 :
518 :
519 : /*****************************************************************************
520 : * We are going to create a whole dataset as a mosaic with all the tiles
521 : ****************************************************************************/
522 0 : if (nTiles == 1 || nMode == ONE_RASTER_PER_TABLE) {
523 :
524 0 : adfGeoTransform[GEOTRSFRM_TOPLEFT_X] = xmin;
525 :
526 : /**
527 : * Negative tilePixelSizeY means that the coords origin is in top left corner.
528 : *
529 : * This is not the common situation. Most image files store data from top to
530 : * bottom, while the projected coordinate systems utilize traditional Cartesian
531 : * coordinates with the origin in the conventional lower-left corner (bottom to
532 : * top). For that reason, this parameter is normally negative.
533 : *
534 : **/
535 :
536 : // Calculate geotransform fields
537 0 : for(i = 0; i < nTiles; i++) {
538 0 : tileSkewX = atof(PQgetvalue(poResult, i, 2));
539 0 : tileSkewY = atof(PQgetvalue(poResult, i, 3));
540 :
541 : // Rotated rasters are not allowed, so far
542 : // TODO: allow them
543 0 : if (!CPLIsEqual(tileSkewX, 0.0) || !CPLIsEqual(tileSkewY, 0.0)) {
544 : CPLError(CE_Failure, CPLE_AppDefined, "GDAL PostGIS Raster driver can not work with "
545 0 : "rotated rasters yet.");
546 :
547 0 : PQclear(poResult);
548 :
549 0 : return false;
550 : }
551 0 : adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] = tileSkewX;
552 0 : adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] = tileSkewY;
553 :
554 0 : tilePixelSizeX = atof(PQgetvalue(poResult, i, 0));
555 0 : tilePixelSizeY = atof(PQgetvalue(poResult, i, 1));
556 :
557 0 : nTileWidth = atoi(PQgetvalue(poResult, i, 4));
558 0 : nTileHeight = atoi(PQgetvalue(poResult, i, 5));
559 :
560 0 : if (bRegularBlocking) {
561 0 : if (nPreviousTileWidth != 0 && nPreviousTileWidth != nTileWidth)
562 0 : bRegularBlocking = false;
563 0 : else if (nPreviousTileHeight != 0 && nPreviousTileHeight != nTileHeight)
564 0 : bRegularBlocking = false;
565 : else {
566 0 : nPreviousTileWidth = nTileWidth;
567 0 : nPreviousTileHeight = nTileHeight;
568 : }
569 : }
570 :
571 : // Calculate pixel size
572 0 : if (resolutionStrategy == AVERAGE_RESOLUTION) {
573 0 : adfGeoTransform[GEOTRSFRM_WE_RES] += tilePixelSizeX;
574 0 : adfGeoTransform[GEOTRSFRM_NS_RES] += tilePixelSizeY;
575 : }
576 :
577 0 : else if (resolutionStrategy == HIGHEST_RESOLUTION) {
578 0 : adfGeoTransform[GEOTRSFRM_WE_RES] = MIN(adfGeoTransform[GEOTRSFRM_WE_RES],
579 0 : atof(PQgetvalue(poResult, i, 0)));
580 :
581 : /* Yes : as ns_res is negative, the highest resolution is the max value */
582 0 : if (tilePixelSizeY < 0.0)
583 0 : adfGeoTransform[GEOTRSFRM_NS_RES] = MAX(adfGeoTransform[GEOTRSFRM_NS_RES],
584 0 : tilePixelSizeY);
585 : else
586 0 : adfGeoTransform[GEOTRSFRM_NS_RES] = MIN(adfGeoTransform[GEOTRSFRM_NS_RES],
587 0 : tilePixelSizeY);
588 : }
589 :
590 0 : else if (resolutionStrategy == LOWEST_RESOLUTION) {
591 0 : adfGeoTransform[GEOTRSFRM_WE_RES] = MAX(adfGeoTransform[GEOTRSFRM_WE_RES],
592 0 : atof(PQgetvalue(poResult, i, 0)));
593 :
594 : /* Yes : as ns_res is negative, the lowest resolution is the min value */
595 0 : if (tilePixelSizeY < 0.0)
596 0 : adfGeoTransform[GEOTRSFRM_NS_RES] = MIN(adfGeoTransform[GEOTRSFRM_NS_RES],
597 0 : tilePixelSizeY);
598 : else
599 0 : adfGeoTransform[GEOTRSFRM_NS_RES] = MAX(adfGeoTransform[GEOTRSFRM_NS_RES],
600 0 : tilePixelSizeY);
601 : }
602 :
603 : // USER_RESOLUTION
604 : else {
605 : // It should be provided by the user. Nothing to do here...
606 : // TODO: Allow the user to provide the resolution (see gdalbuildvrt)
607 : }
608 :
609 : } // end for
610 :
611 0 : if (adfGeoTransform[GEOTRSFRM_NS_RES] >= 0.0)
612 0 : adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] = ymin;
613 : else
614 0 : adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] = ymax;
615 :
616 0 : if (resolutionStrategy == AVERAGE_RESOLUTION) {
617 0 : adfGeoTransform[GEOTRSFRM_WE_RES] /= nTiles;
618 0 : adfGeoTransform[GEOTRSFRM_NS_RES] /= nTiles;
619 : }
620 :
621 0 : nRasterXSize = (int) fabs(rint((xmax - xmin) / adfGeoTransform[GEOTRSFRM_WE_RES]));
622 0 : nRasterYSize = (int) fabs(rint((ymax - ymin) / adfGeoTransform[GEOTRSFRM_NS_RES]));
623 :
624 :
625 0 : if (nRasterXSize <= 0 || nRasterYSize <= 0) {
626 : CPLError(CE_Failure, CPLE_AppDefined,
627 : "Computed PostGIS Raster dimension is invalid. You've probably specified "
628 0 : "unappropriate resolution.");
629 0 : return CE_Failure;
630 : }
631 :
632 : /**
633 : * Regular blocking: get the last values for tile width and height as block
634 : * size
635 : **/
636 0 : if (bRegularBlocking) {
637 0 : nBlockXSize = nTileWidth;
638 0 : nBlockYSize = nTileHeight;
639 : }
640 :
641 0 : PQclear(poResult);
642 :
643 :
644 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
645 : "adfGeoTransform = {%f, %f, %f, %f, %f,%f}", adfGeoTransform[GEOTRSFRM_TOPLEFT_X],
646 : adfGeoTransform[GEOTRSFRM_WE_RES], adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1],
647 : adfGeoTransform[GEOTRSFRM_TOPLEFT_Y],adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2],
648 0 : adfGeoTransform[GEOTRSFRM_NS_RES]);
649 :
650 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
651 0 : "Raster size = (%d, %d)", nRasterXSize, nRasterYSize);
652 :
653 :
654 : /****************************************************************************
655 : * Dataset parameters are set. Now, let's add the raster bands
656 : ***************************************************************************/
657 :
658 : /* Create query to fetch metadata from db */
659 0 : if (pszWhere == NULL) {
660 : osCommand.Printf("select st_bandpixeltype(rast, band), "
661 : "st_bandnodatavalue(rast, band) is null, "
662 : "st_bandnodatavalue(rast, band) from (select %s, "
663 : "generate_series(1, st_numbands(%s)) band from (select "
664 : "rast from %s.%s limit 1) bar) foo",
665 0 : pszColumn, pszColumn, pszSchema, pszTable);
666 : }
667 :
668 : else {
669 : osCommand.Printf("select st_bandpixeltype(rast, band), "
670 : "st_bandnodatavalue(rast, band) is null, "
671 : "st_bandnodatavalue(rast, band) from (select %s, "
672 : "generate_series(1, st_numbands(%s)) band from (select "
673 : "rast from %s.%s where %s limit 1) bar) foo",
674 0 : pszColumn, pszColumn, pszSchema, pszTable, pszWhere);
675 : }
676 :
677 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
678 0 : "Query: %s", osCommand.c_str());
679 :
680 0 : poResult = PQexec(poConn, osCommand.c_str());
681 0 : nTuples = PQntuples(poResult);
682 :
683 : /* Error getting info from database */
684 0 : if (poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK ||
685 : nTuples <= 0) {
686 :
687 : CPLError(CE_Failure, CPLE_AppDefined, "Error getting band metadata "
688 0 : "while creating raster bands");
689 :
690 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): %s",
691 0 : PQerrorMessage(poConn));
692 :
693 0 : if (poResult)
694 0 : PQclear(poResult);
695 :
696 0 : return false;
697 : }
698 :
699 : /* Create each PostGISRasterRasterBand using the band metadata */
700 0 : for (iBand = 0; iBand < nTuples; iBand++) {
701 : /**
702 : * If we have more than one record here is because there are several
703 : * rows, belonging to the same raster coverage, with different band
704 : * metadata values. An error must be raised.
705 : *
706 : * TODO: Is there any way to fix this problem?
707 : *
708 : * TODO: Even when the difference between metadata values are only a
709 : * few decimal numbers (for example: 3.0000000 and 3.0000001) they're
710 : * different tuples. And in that case, they must be the same
711 : **/
712 : /*
713 : if (nTuples > 1) {
714 : CPLError(CE_Failure, CPLE_AppDefined, "Error, the \
715 : ONE_RASTER_PER_TABLE mode can't be applied if the raster \
716 : rows don't have the same metadata for band %d",
717 : iBand + 1);
718 : PQclear(poResult);
719 : return false;
720 : }
721 : */
722 :
723 :
724 : /* Get metadata and create raster band objects */
725 0 : pszDataType = CPLStrdup(PQgetvalue(poResult, iBand, 0));
726 0 : bHasNoDataValue = EQUALN(PQgetvalue(poResult, iBand, 1), "f", sizeof(char));
727 0 : dfNodata = atof(PQgetvalue(poResult, iBand, 2));
728 : /**
729 : * Offline rasters are not yet supported. When offline rasters are
730 : * supported, they will also requires a fast 'getter', other than
731 : * the ST_BandMetaData accessor.
732 : **/
733 :
734 : /* bIsOffline = EQUALN(PQgetvalue(poResult, iBand, 3), "t", sizeof (char)); */
735 :
736 0 : if (EQUALN(pszDataType, "1BB", 3 * sizeof (char))) {
737 0 : hDataType = GDT_Byte;
738 0 : nBitDepth = 1;
739 0 : } else if (EQUALN(pszDataType, "2BUI", 4 * sizeof (char))) {
740 0 : hDataType = GDT_Byte;
741 0 : nBitDepth = 2;
742 0 : } else if (EQUALN(pszDataType, "4BUI", 4 * sizeof (char))) {
743 0 : hDataType = GDT_Byte;
744 0 : nBitDepth = 4;
745 0 : } else if (EQUALN(pszDataType, "8BUI", 4 * sizeof (char))) {
746 0 : hDataType = GDT_Byte;
747 0 : nBitDepth = 8;
748 0 : } else if (EQUALN(pszDataType, "8BSI", 4 * sizeof (char))) {
749 0 : hDataType = GDT_Byte;
750 : /**
751 : * To indicate the unsigned byte values between 128 and 255
752 : * should be interpreted as being values between -128 and -1 for
753 : * applications that recognise the SIGNEDBYTE type.
754 : **/
755 0 : bSignedByte = true;
756 0 : nBitDepth = 8;
757 0 : } else if (EQUALN(pszDataType, "16BSI", 5 * sizeof (char))) {
758 0 : hDataType = GDT_Int16;
759 0 : nBitDepth = 16;
760 0 : } else if (EQUALN(pszDataType, "16BUI", 5 * sizeof (char))) {
761 0 : hDataType = GDT_UInt16;
762 0 : nBitDepth = 16;
763 0 : } else if (EQUALN(pszDataType, "32BSI", 5 * sizeof (char))) {
764 0 : hDataType = GDT_Int32;
765 0 : nBitDepth = 32;
766 0 : } else if (EQUALN(pszDataType, "32BUI", 5 * sizeof (char))) {
767 0 : hDataType = GDT_UInt32;
768 0 : nBitDepth = 32;
769 0 : } else if (EQUALN(pszDataType, "32BF", 4 * sizeof (char))) {
770 0 : hDataType = GDT_Float32;
771 0 : nBitDepth = 32;
772 0 : } else if (EQUALN(pszDataType, "64BF", 4 * sizeof (char))) {
773 0 : hDataType = GDT_Float64;
774 0 : nBitDepth = 64;
775 : } else {
776 0 : hDataType = GDT_Byte;
777 0 : nBitDepth = 8;
778 : }
779 :
780 : /* Create raster band object */
781 : SetBand(iBand + 1, new PostGISRasterRasterBand(this, iBand + 1, hDataType,
782 : bHasNoDataValue, dfNodata, bSignedByte, nBitDepth, 0, nBlockXSize,
783 0 : nBlockYSize, bIsOffline));
784 :
785 0 : CPLFree(pszDataType);
786 : }
787 :
788 0 : PQclear(poResult);
789 : }
790 :
791 :
792 : /*****************************************************************************
793 : * One raster per row: collect subdatasets
794 : ****************************************************************************/
795 : else {
796 : /* Determine the primary key/unique column on the table */
797 : osCommand.Printf("select d.attname from pg_catalog.pg_constraint as a "
798 : "join pg_catalog.pg_indexes as b on a.conname = b.indexname "
799 : "join pg_catalog.pg_class as c on c.relname = b.tablename "
800 : "join pg_catalog.pg_attribute as d on c.relfilenode = d.attrelid "
801 : "where b.schemaname = '%s' and b.tablename = '%s' and "
802 0 : "d.attnum = a.conkey[1] and a.contype in ('p', 'u')", pszSchema, pszTable);
803 :
804 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
805 0 : "Query: %s", osCommand.c_str());
806 :
807 :
808 0 : poResult = PQexec(poConn, osCommand.c_str());
809 0 : if (poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK ||
810 : PQntuples(poResult) <= 0 ) {
811 :
812 0 : PQclear(poResult);
813 :
814 : /*
815 : Maybe there is no primary key or unique constraint;
816 : a sequence will also suffice; get the first one
817 : */
818 :
819 : osCommand.Printf("select cols.column_name from information_schema."
820 : "columns as cols join information_schema.sequences as seqs on cols."
821 : "column_default like '%%'||seqs.sequence_name||'%%' where cols."
822 0 : "table_schema = '%s' and cols.table_name = '%s'", pszSchema, pszTable);
823 :
824 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
825 0 : "Query: %s", osCommand.c_str());
826 :
827 0 : poResult = PQexec(poConn, osCommand.c_str());
828 :
829 0 : if (poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK ||
830 : PQntuples(poResult) <= 0) {
831 :
832 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
833 : "Could not find a primary key or unique column on the specified table; "
834 0 : "using UpperLeftX and UpperLeftY.");
835 :
836 :
837 : /*
838 : If no primary key or unique column found,
839 : fall back to raster upperleft x&y
840 : */
841 : }
842 :
843 : else {
844 0 : pszIdColumn = CPLStrdup(PQgetvalue(poResult, 0, 0));
845 : }
846 :
847 : }
848 :
849 : // Ok, get the primary key
850 : else {
851 0 : pszIdColumn = CPLStrdup(PQgetvalue(poResult, 0, 0));
852 : }
853 :
854 0 : PQclear(poResult);
855 :
856 : /* No primary key on this table. Rely on UpperLeftX and UpperLeftY */
857 0 : if (pszIdColumn == NULL) {
858 0 : if (pszWhere == NULL) {
859 : osCommand.Printf("select ST_UpperLeftX(%s), ST_UpperLeftY(%s) from %s.%s", pszColumn,
860 0 : pszColumn, pszSchema, pszTable);
861 : }
862 :
863 : else {
864 : osCommand.Printf("select ST_UpperLeftX(%s), ST_UpperLeftY(%s) from %s.%s where %s",
865 0 : pszColumn, pszColumn, pszSchema, pszTable, pszWhere);
866 : }
867 :
868 :
869 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
870 0 : "Query: %s", osCommand.c_str());
871 :
872 0 : poResult = PQexec(poConn, osCommand.c_str());
873 0 : if (poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK ||
874 : PQntuples(poResult) <= 0) {
875 :
876 : CPLError(CE_Failure, CPLE_AppDefined, "Error retrieving raster tile metadata "
877 0 : "while creating raster subdatasets");
878 :
879 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
880 0 : "%s", PQerrorMessage(poConn));
881 :
882 0 : if (poResult != NULL)
883 0 : PQclear(poResult);
884 :
885 : // pszIdColumn is already null
886 0 : return false;
887 : }
888 :
889 0 : nTuples = PQntuples(poResult);
890 :
891 : /* Now create the subdatasets */
892 0 : for (i = 0; i < nTuples; i++) {
893 0 : tileUpperLeftX = atof(PQgetvalue(poResult, i, 0)); //upperleft x
894 0 : tileUpperLeftY = atof(PQgetvalue(poResult, i, 1)); //upperleft y
895 :
896 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
897 : CPLSPrintf("SUBDATASET_%d_NAME", (i + 1)),
898 : CPLSPrintf("PG:%s schema=%s table=%s column=%s "
899 : "where='ST_UpperLeftX(%s) = %f AND ST_UpperLeftY(%s) = %f'",
900 : pszValidConnectionString, pszSchema, pszTable, pszColumn,
901 0 : pszColumn, tileUpperLeftX, pszColumn, tileUpperLeftY));
902 :
903 :
904 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
905 : CPLSPrintf("SUBDATASET_%d_DESC", (i + 1)),
906 : CPLSPrintf("PostGIS Raster at %s.%s (%s), UpperLeft = %f, %f", pszSchema,
907 0 : pszTable, pszColumn, tileUpperLeftX, tileUpperLeftY));
908 :
909 : }
910 :
911 0 : PQclear(poResult);
912 : }
913 :
914 : /* There is a primary key */
915 : else {
916 0 : if (pszWhere == NULL) {
917 0 : osCommand.Printf("select %s from %s.%s", pszIdColumn, pszSchema, pszTable);
918 : }
919 :
920 : else {
921 : osCommand.Printf("select %s from %s.%s where %s", pszIdColumn,
922 0 : pszSchema, pszTable, pszWhere);
923 : }
924 :
925 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): "
926 0 : "Query: %s", osCommand.c_str());
927 :
928 0 : poResult = PQexec(poConn, osCommand.c_str());
929 0 : if (poResult == NULL || PQresultStatus(poResult) != PGRES_TUPLES_OK ||
930 : PQntuples(poResult) <= 0) {
931 :
932 : CPLError(CE_Failure, CPLE_AppDefined, "Error retrieving raster row metadata "
933 0 : "while creating raster subdatasets");
934 :
935 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::SetRasterProperties(): %s",
936 0 : PQerrorMessage(poConn));
937 :
938 :
939 0 : if (poResult != NULL)
940 0 : PQclear(poResult);
941 :
942 0 : if (pszIdColumn != NULL)
943 0 : CPLFree(pszIdColumn);
944 :
945 0 : return false;
946 :
947 : }
948 :
949 :
950 0 : nTuples = PQntuples(poResult);
951 :
952 : /* Now, create the subdatasets */
953 0 : for (i = 0; i < nTuples; i++) {
954 : // this is the raster ID (or unique column)
955 0 : nRasterID = atoi(PQgetvalue(poResult, i, 0));
956 :
957 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
958 : CPLSPrintf("SUBDATASET_%d_NAME", (i + 1)),
959 : CPLSPrintf("PG:%s schema=%s table=%s column=%s where='%s = %d'",
960 : pszValidConnectionString, pszSchema, pszTable, pszColumn,
961 0 : pszIdColumn, nRasterID));
962 :
963 :
964 : papszSubdatasets = CSLSetNameValue(papszSubdatasets,
965 : CPLSPrintf("SUBDATASET_%d_DESC", (i + 1)),
966 : CPLSPrintf("PostGIS Raster at %s.%s (%s), %s = %d", pszSchema,
967 0 : pszTable, pszColumn, pszIdColumn, nRasterID));
968 : }
969 :
970 0 : PQclear(poResult);
971 : }
972 :
973 : /**
974 : * Not a single raster fetched. Not really needed. Just to keep code clean
975 : **/
976 0 : nRasterXSize = 0;
977 0 : nRasterYSize = 0;
978 0 : adfGeoTransform[GEOTRSFRM_TOPLEFT_X] = 0.0;
979 0 : adfGeoTransform[GEOTRSFRM_WE_RES] = 1.0;
980 0 : adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] = 0.0;
981 0 : adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] = 0.0;
982 0 : adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] = 0.0;
983 0 : adfGeoTransform[GEOTRSFRM_NS_RES] = -1.0;
984 :
985 0 : if (pszIdColumn != NULL)
986 0 : CPLFree(pszIdColumn);
987 : }
988 :
989 :
990 0 : return true;
991 : }
992 :
993 : /******************************************************************************
994 : * \brief Get the connection information for a filename.
995 : ******************************************************************************/
996 : static GBool
997 1 : GetConnectionInfo(const char * pszFilename,
998 : char ** ppszConnectionString, char ** ppszSchema, char ** ppszTable,
999 : char ** ppszColumn, char ** ppszWhere, char ** ppszHost,
1000 : char ** ppszPort, char ** ppszUser, char ** ppszPassword,
1001 : int * nMode, GBool * bBrowseDatabase)
1002 : {
1003 1 : int nPos = -1, i;
1004 1 : char * pszTmp = NULL;
1005 1 : char **papszParams = ParseConnectionString(pszFilename);
1006 1 : if (papszParams == NULL) {
1007 0 : return false;
1008 : }
1009 :
1010 : /**************************************************************************
1011 : * Get mode:
1012 : * - 1. ONE_RASTER_PER_ROW: Each row is considered as a separate raster
1013 : * - 2. ONE_RASTER_PER_TABLE: All the table rows are considered as a whole
1014 : * raster coverage
1015 : **************************************************************************/
1016 1 : nPos = CSLFindName(papszParams, "mode");
1017 1 : if (nPos != -1) {
1018 0 : *nMode = atoi(CPLParseNameValue(papszParams[nPos], NULL));
1019 :
1020 0 : if (*nMode != ONE_RASTER_PER_ROW && *nMode != ONE_RASTER_PER_TABLE) {
1021 : /* Unrecognized mode, using default one */
1022 : /*
1023 : CPLError(CE_Warning, CPLE_AppDefined, "Undefined working mode (%d)."
1024 : " Valid working modes are 1 (ONE_RASTER_PER_ROW) and 2"
1025 : " (ONE_RASTER_PER_TABLE). Using ONE_RASTER_PER_TABLE"
1026 : " by default", nMode);
1027 : */
1028 0 : *nMode = ONE_RASTER_PER_ROW;
1029 : }
1030 :
1031 : /* Remove the mode from connection string */
1032 0 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1033 : }
1034 : /* Default mode */
1035 : else
1036 1 : *nMode = ONE_RASTER_PER_ROW;
1037 :
1038 : /**
1039 : * Case 1: There's no database name: Error, you need, at least,
1040 : * specify a database name (NOTE: insensitive search)
1041 : **/
1042 1 : nPos = CSLFindName(papszParams, "dbname");
1043 1 : if (nPos == -1) {
1044 : CPLError(CE_Failure, CPLE_AppDefined,
1045 0 : "You must specify at least a db name");
1046 :
1047 0 : CSLDestroy(papszParams);
1048 :
1049 0 : return false;
1050 : }
1051 :
1052 : /**
1053 : * Case 2: There's database name, but no table name: activate a flag
1054 : * for browsing the database, fetching all the schemas that contain
1055 : * raster tables
1056 : **/
1057 1 : nPos = CSLFindName(papszParams, "table");
1058 1 : if (nPos == -1) {
1059 0 : *bBrowseDatabase = true;
1060 :
1061 : /* Get schema name, if exist */
1062 0 : nPos = CSLFindName(papszParams, "schema");
1063 0 : if (nPos != -1) {
1064 0 : *ppszSchema = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1065 : /* Delete this pair from params array */
1066 0 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1067 : }
1068 :
1069 : /**
1070 : * Remove the rest of the parameters, if exist (they mustn't be present
1071 : * if we want a valid PQ connection string)
1072 : **/
1073 0 : nPos = CSLFindName(papszParams, "column");
1074 0 : if (nPos != -1) {
1075 : /* Delete this pair from params array */
1076 0 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1077 : }
1078 :
1079 0 : nPos = CSLFindName(papszParams, "where");
1080 0 : if (nPos != -1) {
1081 : /* Delete this pair from params array */
1082 0 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1083 : }
1084 : } else {
1085 1 : *ppszTable = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1086 : /* Delete this pair from params array */
1087 1 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1088 :
1089 : /**
1090 : * Case 3: There's database and table name, but no column
1091 : * name: Use a default column name and use the table to create the
1092 : * dataset
1093 : **/
1094 1 : nPos = CSLFindName(papszParams, "column");
1095 1 : if (nPos == -1) {
1096 1 : *ppszColumn = CPLStrdup(DEFAULT_COLUMN);
1097 : }
1098 : /**
1099 : * Case 4: There's database, table and column name: Use the table to
1100 : * create a dataset
1101 : **/
1102 : else {
1103 0 : *ppszColumn = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1104 : /* Delete this pair from params array */
1105 0 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1106 : }
1107 :
1108 : /* Get the rest of the parameters, if exist */
1109 1 : nPos = CSLFindName(papszParams, "schema");
1110 1 : if (nPos == -1) {
1111 0 : *ppszSchema = CPLStrdup(DEFAULT_SCHEMA);
1112 : } else {
1113 1 : *ppszSchema = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1114 : /* Delete this pair from params array */
1115 1 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1116 : }
1117 :
1118 1 : nPos = CSLFindName(papszParams, "where");
1119 1 : if (nPos != -1) {
1120 0 : *ppszWhere = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1121 : /* Delete this pair from params array */
1122 0 : papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL);
1123 : }
1124 : }
1125 :
1126 : /* Parse ppszWhere, if needed */
1127 1 : if (*ppszWhere) {
1128 0 : pszTmp = ReplaceQuotes(*ppszWhere, strlen(*ppszWhere));
1129 0 : CPLFree(*ppszWhere);
1130 0 : *ppszWhere = pszTmp;
1131 : }
1132 :
1133 : /***************************************
1134 : * Construct a valid connection string
1135 : ***************************************/
1136 : *ppszConnectionString = (char*) CPLCalloc(strlen(pszFilename),
1137 1 : sizeof (char));
1138 5 : for (i = 0; i < CSLCount(papszParams); i++) {
1139 4 : *ppszConnectionString = strncat(*ppszConnectionString, papszParams[i], strlen(papszParams[i]));
1140 4 : *ppszConnectionString = strncat(*ppszConnectionString, " ", strlen(" "));
1141 : }
1142 :
1143 1 : nPos = CSLFindName(papszParams, "host");
1144 1 : if (nPos != -1) {
1145 1 : *ppszHost = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1146 : }
1147 0 : else if (getenv("PGHOST") != NULL) {
1148 0 : *ppszHost = CPLStrdup(getenv("PGHOST"));
1149 : }
1150 : else {
1151 : CPLError(CE_Failure, CPLE_AppDefined,
1152 : "Host parameter must be provided, or PGHOST environment "
1153 0 : "variable must be set. Please set the host and try again.");
1154 :
1155 0 : CSLDestroy(papszParams);
1156 :
1157 0 : return false;
1158 : }
1159 :
1160 1 : nPos = CSLFindName(papszParams, "port");
1161 1 : if (nPos != -1) {
1162 0 : *ppszPort = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1163 : }
1164 1 : else if (getenv("PGPORT") != NULL ) {
1165 0 : *ppszPort = CPLStrdup(getenv("PGPORT"));
1166 : }
1167 : else {
1168 : CPLError(CE_Failure, CPLE_AppDefined,
1169 : "Port parameter must be provided, or PGPORT environment "
1170 1 : "variable must be set. Please set the port and try again.");
1171 :
1172 1 : CSLDestroy(papszParams);
1173 :
1174 1 : return false;
1175 : }
1176 :
1177 0 : nPos = CSLFindName(papszParams, "user");
1178 0 : if (nPos != -1) {
1179 0 : *ppszUser = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1180 : }
1181 0 : else if (getenv("PGUSER") != NULL ) {
1182 0 : *ppszUser = CPLStrdup(getenv("PGUSER"));
1183 : }
1184 : else {
1185 : CPLError(CE_Failure, CPLE_AppDefined,
1186 : "User parameter must be provided, or PGUSER environment "
1187 0 : "variable must be set. Please set the user and try again.");
1188 :
1189 0 : CSLDestroy(papszParams);
1190 :
1191 0 : return false;
1192 : }
1193 :
1194 0 : nPos = CSLFindName(papszParams, "password");
1195 0 : if (nPos != -1) {
1196 0 : *ppszPassword = CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL));
1197 : }
1198 : else {
1199 : // if PGPASSWORD is not set, ppszPassword is set to an empty string.
1200 : // this is okay, since there may be configurations in pg_hba.conf
1201 : // that don't require any password to connect
1202 0 : *ppszPassword = CPLStrdup(getenv("PGPASSWORD"));
1203 : }
1204 :
1205 0 : CSLDestroy(papszParams);
1206 :
1207 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::GetConnectionInfo(): "
1208 : "Mode: %d\nSchema: %s\nTable: %s\nColumn: %s\nWhere: %s\n"
1209 : "Host: %s\nPort: %s\nUser: %s\nPassword: %s\nConnection String: %s",
1210 : *nMode, *ppszSchema, *ppszTable, *ppszColumn,
1211 0 : *ppszWhere, *ppszHost, *ppszPort, *ppszUser, *ppszPassword, *ppszConnectionString);
1212 :
1213 0 : return true;
1214 : }
1215 :
1216 : /******************************************************************************
1217 : * \brief Create a connection to a postgres database
1218 : ******************************************************************************/
1219 : static PGconn *
1220 1 : GetConnection(const char * pszFilename, char ** ppszConnectionString,
1221 : char ** ppszSchema, char ** ppszTable, char ** ppszColumn, char ** ppszWhere,
1222 : int * nMode, GBool * bBrowseDatabase)
1223 : {
1224 : PostGISRasterDriver * poDriver;
1225 1 : PGconn * poConn = NULL;
1226 1 : char * pszHost = NULL;
1227 1 : char * pszPort = NULL;
1228 1 : char * pszUser = NULL;
1229 1 : char * pszPassword = NULL;
1230 :
1231 1 : if (GetConnectionInfo(pszFilename, ppszConnectionString, ppszSchema,
1232 : ppszTable, ppszColumn, ppszWhere, &pszHost, &pszPort, &pszUser,
1233 : &pszPassword, nMode, bBrowseDatabase))
1234 : {
1235 : /********************************************************************
1236 : * Open a new database connection
1237 : ********************************************************************/
1238 0 : poDriver = (PostGISRasterDriver *)GDALGetDriverByName("PostGISRaster");
1239 : poConn = poDriver->GetConnection(*ppszConnectionString,
1240 0 : pszHost, pszPort, pszUser, pszPassword);
1241 :
1242 0 : if (poConn == NULL) {
1243 : CPLError(CE_Failure, CPLE_AppDefined,
1244 0 : "Couldn't establish a database connection");
1245 : }
1246 : }
1247 :
1248 1 : CPLFree(pszHost);
1249 1 : CPLFree(pszPort);
1250 1 : CPLFree(pszUser);
1251 1 : CPLFree(pszPassword);
1252 :
1253 1 : return poConn;
1254 : }
1255 :
1256 :
1257 : /******************************************************************************
1258 : * \brief Open a connection with PostgreSQL. The connection string will have
1259 : * the PostgreSQL accepted format, plus the next key=value pairs:
1260 : * schema = <schema_name>
1261 : * table = <table_name>
1262 : * column = <column_name>
1263 : * where = <SQL where>
1264 : * mode = <working mode> (1 or 2)
1265 : *
1266 : * These pairs are used for selecting the right raster table.
1267 : *****************************************************************************/
1268 11736 : GDALDataset* PostGISRasterDataset::Open(GDALOpenInfo* poOpenInfo) {
1269 11736 : char* pszConnectionString = NULL;
1270 11736 : char* pszSchema = NULL;
1271 11736 : char* pszTable = NULL;
1272 11736 : char* pszColumn = NULL;
1273 11736 : char* pszWhere = NULL;
1274 11736 : int nMode = -1;
1275 11736 : PGconn * poConn = NULL;
1276 11736 : PostGISRasterDataset* poDS = NULL;
1277 11736 : GBool bBrowseDatabase = false;
1278 11736 : CPLString osCommand;
1279 : char * pszTmp;
1280 :
1281 : /**************************
1282 : * Check input parameter
1283 : **************************/
1284 11736 : if (poOpenInfo->pszFilename == NULL ||
1285 : poOpenInfo->fp != NULL ||
1286 : !EQUALN(poOpenInfo->pszFilename, "PG:", 3))
1287 : {
1288 : /**
1289 : * Drivers must quietly return NULL if the passed file is not of
1290 : * their format. They should only produce an error if the file
1291 : * does appear to be of their supported format, but for some
1292 : * reason, unsupported or corrupt
1293 : */
1294 11735 : return NULL;
1295 : }
1296 :
1297 1 : pszTmp = CPLStrdup(poOpenInfo->pszFilename);
1298 :
1299 : poConn = GetConnection((char *)poOpenInfo->pszFilename,
1300 : &pszConnectionString, &pszSchema, &pszTable, &pszColumn, &pszWhere,
1301 1 : &nMode, &bBrowseDatabase);
1302 1 : if (poConn == NULL) {
1303 1 : CPLFree(pszConnectionString);
1304 1 : CPLFree(pszSchema);
1305 1 : CPLFree(pszTable);
1306 1 : CPLFree(pszColumn);
1307 1 : CPLFree(pszWhere);
1308 1 : return NULL;
1309 : }
1310 :
1311 :
1312 : /*************************************************************************
1313 : * No table will be read. Only shows information about the existent raster
1314 : * tables
1315 : *************************************************************************/
1316 0 : if (bBrowseDatabase) {
1317 : /**
1318 : * Creates empty dataset object, only for subdatasets
1319 : **/
1320 0 : poDS = new PostGISRasterDataset();
1321 0 : poDS->poConn = poConn;
1322 0 : poDS->eAccess = GA_ReadOnly;
1323 : //poDS->poDriver = poDriver;
1324 0 : poDS->nMode = (pszSchema) ? BROWSE_SCHEMA : BROWSE_DATABASE;
1325 0 : poDS->nRasterXSize = 0;
1326 0 : poDS->nRasterYSize = 0;
1327 0 : poDS->adfGeoTransform[0] = 0.0;
1328 0 : poDS->adfGeoTransform[1] = 0.0;
1329 0 : poDS->adfGeoTransform[2] = 0.0;
1330 0 : poDS->adfGeoTransform[3] = 0.0;
1331 0 : poDS->adfGeoTransform[4] = 0.0;
1332 0 : poDS->adfGeoTransform[5] = 0.0;
1333 :
1334 : /**
1335 : * Look for raster tables at database and
1336 : * store them as subdatasets
1337 : **/
1338 0 : if (!poDS->BrowseDatabase(pszSchema, pszConnectionString)) {
1339 0 : CPLFree(pszConnectionString);
1340 0 : delete poDS;
1341 :
1342 0 : if (pszSchema)
1343 0 : CPLFree(pszSchema);
1344 0 : if (pszTable)
1345 0 : CPLFree(pszTable);
1346 0 : if (pszColumn)
1347 0 : CPLFree(pszColumn);
1348 0 : if (pszWhere)
1349 0 : CPLFree(pszWhere);
1350 :
1351 0 : return NULL;
1352 : }
1353 :
1354 0 : if (pszSchema)
1355 0 : CPLFree(pszSchema);
1356 0 : if (pszTable)
1357 0 : CPLFree(pszTable);
1358 0 : if (pszColumn)
1359 0 : CPLFree(pszColumn);
1360 0 : if (pszWhere)
1361 0 : CPLFree(pszWhere);
1362 : }
1363 : /***********************************************************************
1364 : * A table will be read: Fetch raster properties from db. Pay attention
1365 : * to the block size: if the raster is blocked at database, the block
1366 : * size can be fetched from each block size, if regular blocking table
1367 : **********************************************************************/
1368 : else {
1369 0 : poDS = new PostGISRasterDataset();
1370 0 : poDS->poConn = poConn;
1371 0 : poDS->eAccess = poOpenInfo->eAccess;
1372 0 : poDS->nMode = nMode;
1373 : //poDS->poDriver = poDriver;
1374 :
1375 0 : poDS->pszSchema = pszSchema;
1376 0 : poDS->pszTable = pszTable;
1377 0 : poDS->pszColumn = pszColumn;
1378 0 : poDS->pszWhere = pszWhere;
1379 :
1380 : /**
1381 : * Fetch basic raster metadata from db
1382 : **/
1383 :
1384 : CPLDebug("PostGIS_Raster", "Open:: connection string = %s",
1385 0 : pszConnectionString);
1386 :
1387 0 : if (!poDS->SetRasterProperties(pszConnectionString)) {
1388 0 : CPLFree(pszConnectionString);
1389 0 : delete poDS;
1390 0 : return NULL;
1391 : }
1392 :
1393 0 : poDS->pszOriginalConnectionString = pszTmp;
1394 :
1395 : CPLDebug("PostGIS_Raster", "Open:: original connection string = %s",
1396 0 : poDS->pszOriginalConnectionString);
1397 :
1398 : }
1399 :
1400 0 : CPLFree(pszConnectionString);
1401 0 : return poDS;
1402 :
1403 : }
1404 :
1405 : /*****************************************
1406 : * \brief Get Metadata from raster
1407 : * TODO: Add more options (the result of
1408 : * calling ST_Metadata, for example)
1409 : *****************************************/
1410 0 : char** PostGISRasterDataset::GetMetadata(const char *pszDomain) {
1411 0 : if (pszDomain != NULL && EQUALN(pszDomain, "SUBDATASETS", 11))
1412 0 : return papszSubdatasets;
1413 : else
1414 0 : return GDALDataset::GetMetadata(pszDomain);
1415 : }
1416 :
1417 : /*****************************************************
1418 : * \brief Fetch the projection definition string
1419 : * for this dataset in OpenGIS WKT format. It should
1420 : * be suitable for use with the OGRSpatialReference
1421 : * class.
1422 : *****************************************************/
1423 0 : const char* PostGISRasterDataset::GetProjectionRef() {
1424 0 : CPLString osCommand;
1425 : PGresult* poResult;
1426 :
1427 0 : if (nSrid == -1)
1428 0 : return "";
1429 :
1430 0 : if (pszProjection)
1431 0 : return pszProjection;
1432 :
1433 : /********************************************************
1434 : * Reading proj from database
1435 : ********************************************************/
1436 : osCommand.Printf("SELECT srtext FROM spatial_ref_sys where SRID=%d",
1437 0 : nSrid);
1438 0 : poResult = PQexec(this->poConn, osCommand.c_str());
1439 0 : if (poResult && PQresultStatus(poResult) == PGRES_TUPLES_OK
1440 : && PQntuples(poResult) > 0) {
1441 : /*
1442 : * TODO: Memory leak detected with valgrind caused by allocation here.
1443 : * Even when the return string is freed
1444 : */
1445 0 : pszProjection = CPLStrdup(PQgetvalue(poResult, 0, 0));
1446 : }
1447 :
1448 0 : if (poResult)
1449 0 : PQclear(poResult);
1450 :
1451 0 : return pszProjection;
1452 : }
1453 :
1454 : /**********************************************************
1455 : * \brief Set projection definition. The input string must
1456 : * be in OGC WKT or PROJ.4 format
1457 : **********************************************************/
1458 0 : CPLErr PostGISRasterDataset::SetProjection(const char * pszProjectionRef) {
1459 0 : VALIDATE_POINTER1(pszProjectionRef, "SetProjection", CE_Failure);
1460 :
1461 0 : CPLString osCommand;
1462 : PGresult * poResult;
1463 0 : int nFetchedSrid = -1;
1464 :
1465 :
1466 : /*****************************************************************
1467 : * Check if the dataset allows updating
1468 : *****************************************************************/
1469 0 : if (GetAccess() != GA_Update) {
1470 : CPLError(CE_Failure, CPLE_NoWriteAccess,
1471 0 : "This driver doesn't allow write access");
1472 0 : return CE_Failure;
1473 : }
1474 :
1475 : /*****************************************************************
1476 : * Look for projection with this text
1477 : *****************************************************************/
1478 :
1479 : // First, WKT text
1480 : osCommand.Printf("SELECT srid FROM spatial_ref_sys where srtext='%s'",
1481 0 : pszProjectionRef);
1482 0 : poResult = PQexec(poConn, osCommand.c_str());
1483 :
1484 0 : if (poResult && PQresultStatus(poResult) == PGRES_TUPLES_OK
1485 : && PQntuples(poResult) > 0) {
1486 :
1487 0 : nFetchedSrid = atoi(PQgetvalue(poResult, 0, 0));
1488 :
1489 : // update class attribute
1490 0 : nSrid = nFetchedSrid;
1491 :
1492 :
1493 : // update raster_columns table
1494 : osCommand.Printf("UPDATE raster_columns SET srid=%d WHERE \
1495 : r_table_name = '%s' AND r_column = '%s'",
1496 0 : nSrid, pszTable, pszColumn);
1497 0 : poResult = PQexec(poConn, osCommand.c_str());
1498 0 : if (poResult == NULL || PQresultStatus(poResult) != PGRES_COMMAND_OK) {
1499 : CPLError(CE_Failure, CPLE_AppDefined,
1500 : "Couldn't update raster_columns table: %s",
1501 0 : PQerrorMessage(poConn));
1502 0 : return CE_Failure;
1503 : }
1504 :
1505 : // TODO: Update ALL blocks with the new srid...
1506 :
1507 0 : return CE_None;
1508 : }
1509 : // If not, proj4 text
1510 : else {
1511 : osCommand.Printf(
1512 : "SELECT srid FROM spatial_ref_sys where proj4text='%s'",
1513 0 : pszProjectionRef);
1514 0 : poResult = PQexec(poConn, osCommand.c_str());
1515 :
1516 0 : if (poResult && PQresultStatus(poResult) == PGRES_TUPLES_OK
1517 : && PQntuples(poResult) > 0) {
1518 :
1519 0 : nFetchedSrid = atoi(PQgetvalue(poResult, 0, 0));
1520 :
1521 : // update class attribute
1522 0 : nSrid = nFetchedSrid;
1523 :
1524 : // update raster_columns table
1525 : osCommand.Printf("UPDATE raster_columns SET srid=%d WHERE \
1526 : r_table_name = '%s' AND r_column = '%s'",
1527 0 : nSrid, pszTable, pszColumn);
1528 :
1529 0 : poResult = PQexec(poConn, osCommand.c_str());
1530 0 : if (poResult == NULL ||
1531 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
1532 : CPLError(CE_Failure, CPLE_AppDefined,
1533 : "Couldn't update raster_columns table: %s",
1534 0 : PQerrorMessage(poConn));
1535 0 : return CE_Failure;
1536 : }
1537 :
1538 : // TODO: Update ALL blocks with the new srid...
1539 :
1540 0 : return CE_None;
1541 : }
1542 : else {
1543 : CPLError(CE_Failure, CPLE_WrongFormat,
1544 0 : "Couldn't find WKT neither proj4 definition");
1545 0 : return CE_Failure;
1546 : }
1547 0 : }
1548 : }
1549 :
1550 : /********************************************************
1551 : * \brief Set the affine transformation coefficients
1552 : ********************************************************/
1553 0 : CPLErr PostGISRasterDataset::SetGeoTransform(double* padfTransform) {
1554 0 : if (!padfTransform)
1555 0 : return CE_Failure;
1556 :
1557 0 : adfGeoTransform[0] = padfTransform[0];
1558 0 : adfGeoTransform[1] = padfTransform[1];
1559 0 : adfGeoTransform[2] = padfTransform[2];
1560 0 : adfGeoTransform[3] = padfTransform[3];
1561 0 : adfGeoTransform[4] = padfTransform[4];
1562 0 : adfGeoTransform[5] = padfTransform[5];
1563 :
1564 0 : return CE_None;
1565 : }
1566 :
1567 : /********************************************************
1568 : * \brief Get the affine transformation coefficients
1569 : ********************************************************/
1570 0 : CPLErr PostGISRasterDataset::GetGeoTransform(double * padfTransform) {
1571 : // copy necessary values in supplied buffer
1572 0 : padfTransform[0] = adfGeoTransform[0];
1573 0 : padfTransform[1] = adfGeoTransform[1];
1574 0 : padfTransform[2] = adfGeoTransform[2];
1575 0 : padfTransform[3] = adfGeoTransform[3];
1576 0 : padfTransform[4] = adfGeoTransform[4];
1577 0 : padfTransform[5] = adfGeoTransform[5];
1578 :
1579 0 : return CE_None;
1580 : }
1581 :
1582 : /********************************************************
1583 : * \brief Create a copy of a PostGIS Raster dataset.
1584 : ********************************************************/
1585 : GDALDataset *
1586 18 : PostGISRasterDataset::CreateCopy( const char * pszFilename,
1587 : GDALDataset *poGSrcDS, int bStrict, char ** papszOptions,
1588 : GDALProgressFunc pfnProgress, void * pProgressData )
1589 : {
1590 18 : char* pszSchema = NULL;
1591 18 : char* pszTable = NULL;
1592 18 : char* pszColumn = NULL;
1593 18 : char* pszWhere = NULL;
1594 18 : GBool bBrowseDatabase = false;
1595 : int nMode;
1596 18 : char* pszConnectionString = NULL;
1597 : const char* pszSubdatasetName;
1598 18 : PGconn * poConn = NULL;
1599 18 : PGresult * poResult = NULL;
1600 18 : CPLString osCommand;
1601 : GBool bInsertSuccess;
1602 18 : PostGISRasterDataset *poSrcDS = (PostGISRasterDataset *)poGSrcDS;
1603 : PostGISRasterDataset *poSubDS;
1604 :
1605 : // Check connection string
1606 18 : if (pszFilename == NULL ||
1607 : !EQUALN(pszFilename, "PG:", 3)) {
1608 : /**
1609 : * The connection string provided is not a valid connection
1610 : * string.
1611 : */
1612 : CPLError( CE_Failure, CPLE_NotSupported,
1613 : "PostGIS Raster driver was unable to parse the provided "
1614 18 : "connection string." );
1615 18 : return NULL;
1616 : }
1617 :
1618 : poConn = GetConnection(pszFilename, &pszConnectionString, &pszSchema,
1619 0 : &pszTable, &pszColumn, &pszWhere, &nMode, &bBrowseDatabase);
1620 0 : if (poConn == NULL || bBrowseDatabase || pszTable == NULL)
1621 : {
1622 0 : CPLFree(pszConnectionString);
1623 0 : CPLFree(pszSchema);
1624 0 : CPLFree(pszTable);
1625 0 : CPLFree(pszColumn);
1626 0 : CPLFree(pszWhere);
1627 :
1628 : // if connection info fails, browsing mode, or no table set
1629 0 : return NULL;
1630 : }
1631 :
1632 : // begin transaction
1633 0 : poResult = PQexec(poConn, "begin");
1634 0 : if (poResult == NULL ||
1635 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
1636 : CPLError(CE_Failure, CPLE_AppDefined,
1637 : "Error beginning database transaction: %s",
1638 0 : PQerrorMessage(poConn));
1639 0 : if (poResult != NULL)
1640 0 : PQclear(poResult);
1641 0 : if (pszSchema)
1642 0 : CPLFree(pszSchema);
1643 0 : if (pszTable)
1644 0 : CPLFree(pszTable);
1645 0 : if (pszColumn)
1646 0 : CPLFree(pszColumn);
1647 0 : if (pszWhere)
1648 0 : CPLFree(pszWhere);
1649 :
1650 0 : CPLFree(pszConnectionString);
1651 :
1652 0 : return NULL;
1653 : }
1654 :
1655 0 : PQclear(poResult);
1656 :
1657 : // create table for raster (if not exists because a
1658 : // dataset will not be reported for an empty table)
1659 :
1660 : // TODO: is 'rid' necessary?
1661 : osCommand.Printf("create table if not exists %s.%s (rid serial, %s "
1662 : "public.raster, constraint %s_pkey primary key (rid));",
1663 0 : pszSchema, pszTable, pszColumn, pszTable);
1664 0 : poResult = PQexec(poConn, osCommand.c_str());
1665 0 : if (
1666 : poResult == NULL ||
1667 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
1668 :
1669 : CPLError(CE_Failure, CPLE_AppDefined,
1670 : "Error creating needed tables: %s",
1671 0 : PQerrorMessage(poConn));
1672 0 : if (poResult != NULL)
1673 0 : PQclear(poResult);
1674 :
1675 : // rollback
1676 0 : poResult = PQexec(poConn, "rollback");
1677 0 : if (poResult == NULL ||
1678 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
1679 :
1680 : CPLError(CE_Failure, CPLE_AppDefined,
1681 : "Error rolling back transaction: %s",
1682 0 : PQerrorMessage(poConn));
1683 : }
1684 0 : if (poResult != NULL)
1685 0 : PQclear(poResult);
1686 0 : if (pszSchema)
1687 0 : CPLFree(pszSchema);
1688 0 : if (pszTable)
1689 0 : CPLFree(pszTable);
1690 0 : if (pszColumn)
1691 0 : CPLFree(pszColumn);
1692 0 : if (pszWhere)
1693 0 : CPLFree(pszWhere);
1694 :
1695 0 : CPLFree(pszConnectionString);
1696 :
1697 0 : return NULL;
1698 : }
1699 :
1700 0 : PQclear(poResult);
1701 :
1702 : osCommand.Printf("create index %s_%s_gist ON %s.%s USING gist "
1703 : "(public.st_convexhull(%s));", pszTable, pszColumn,
1704 0 : pszSchema, pszTable, pszColumn);
1705 0 : poResult = PQexec(poConn, osCommand.c_str());
1706 0 : if (
1707 : poResult == NULL ||
1708 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
1709 :
1710 : CPLError(CE_Failure, CPLE_AppDefined,
1711 : "Error creating needed index: %s",
1712 0 : PQerrorMessage(poConn));
1713 0 : if (poResult != NULL)
1714 0 : PQclear(poResult);
1715 :
1716 : // rollback
1717 0 : poResult = PQexec(poConn, "rollback");
1718 0 : if (poResult == NULL ||
1719 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
1720 :
1721 : CPLError(CE_Failure, CPLE_AppDefined,
1722 : "Error rolling back transaction: %s",
1723 0 : PQerrorMessage(poConn));
1724 : }
1725 0 : if (poResult != NULL)
1726 0 : PQclear(poResult);
1727 0 : if (pszSchema)
1728 0 : CPLFree(pszSchema);
1729 0 : if (pszTable)
1730 0 : CPLFree(pszTable);
1731 0 : if (pszColumn)
1732 0 : CPLFree(pszColumn);
1733 0 : if (pszWhere)
1734 0 : CPLFree(pszWhere);
1735 :
1736 0 : CPLFree(pszConnectionString);
1737 :
1738 0 : return NULL;
1739 : }
1740 :
1741 0 : PQclear(poResult);
1742 :
1743 0 : if (poSrcDS->nMode == ONE_RASTER_PER_TABLE) {
1744 : // one raster per table
1745 :
1746 : // insert one raster
1747 : bInsertSuccess = InsertRaster(poConn, poSrcDS,
1748 0 : pszSchema, pszTable, pszColumn);
1749 0 : if (!bInsertSuccess) {
1750 : // rollback
1751 0 : poResult = PQexec(poConn, "rollback");
1752 0 : if (poResult == NULL ||
1753 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
1754 :
1755 : CPLError(CE_Failure, CPLE_AppDefined,
1756 : "Error rolling back transaction: %s",
1757 0 : PQerrorMessage(poConn));
1758 : }
1759 0 : if (poResult != NULL)
1760 0 : PQclear(poResult);
1761 0 : if (pszSchema)
1762 0 : CPLFree(pszSchema);
1763 0 : if (pszTable)
1764 0 : CPLFree(pszTable);
1765 0 : if (pszColumn)
1766 0 : CPLFree(pszColumn);
1767 0 : if (pszWhere)
1768 0 : CPLFree(pszWhere);
1769 :
1770 0 : CPLFree(pszConnectionString);
1771 :
1772 0 : return NULL;
1773 : }
1774 : }
1775 0 : else if (poSrcDS->nMode == ONE_RASTER_PER_ROW) {
1776 : // one raster per row
1777 :
1778 : // papszSubdatasets contains name/desc for each subdataset
1779 0 : for (int i = 0; i < CSLCount(poSrcDS->papszSubdatasets); i += 2) {
1780 0 : pszSubdatasetName = CPLParseNameValue( poSrcDS->papszSubdatasets[i], NULL);
1781 0 : if (pszSubdatasetName == NULL) {
1782 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): "
1783 : "Could not parse name/value out of subdataset list: "
1784 0 : "%s", poSrcDS->papszSubdatasets[i]);
1785 0 : continue;
1786 : }
1787 :
1788 : // for each subdataset
1789 0 : GDALOpenInfo poOpenInfo( pszSubdatasetName, GA_ReadOnly );
1790 : // open the subdataset
1791 0 : poSubDS = (PostGISRasterDataset *)Open(&poOpenInfo);
1792 :
1793 0 : if (poSubDS == NULL) {
1794 : // notify!
1795 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): "
1796 : "Could not open a subdataset: %s",
1797 0 : pszSubdatasetName);
1798 0 : continue;
1799 : }
1800 :
1801 : // insert one raster
1802 : bInsertSuccess = InsertRaster(poConn, poSubDS,
1803 0 : pszSchema, pszTable, pszColumn);
1804 :
1805 0 : if (!bInsertSuccess) {
1806 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): "
1807 0 : "Could not copy raster subdataset to new dataset." );
1808 :
1809 : // keep trying ...
1810 : }
1811 :
1812 : // close this dataset
1813 0 : GDALClose((GDALDatasetH)poSubDS);
1814 : }
1815 : }
1816 :
1817 : // commit transaction
1818 0 : poResult = PQexec(poConn, "commit");
1819 0 : if (poResult == NULL ||
1820 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
1821 : CPLError(CE_Failure, CPLE_AppDefined,
1822 : "Error committing database transaction: %s",
1823 0 : PQerrorMessage(poConn));
1824 0 : if (poResult != NULL)
1825 0 : PQclear(poResult);
1826 0 : if (pszSchema)
1827 0 : CPLFree(pszSchema);
1828 0 : if (pszTable)
1829 0 : CPLFree(pszTable);
1830 0 : if (pszColumn)
1831 0 : CPLFree(pszColumn);
1832 0 : if (pszWhere)
1833 0 : CPLFree(pszWhere);
1834 :
1835 0 : CPLFree(pszConnectionString);
1836 :
1837 0 : return NULL;
1838 : }
1839 :
1840 0 : PQclear(poResult);
1841 :
1842 0 : if (pszSchema)
1843 0 : CPLFree(pszSchema);
1844 0 : if (pszTable)
1845 0 : CPLFree(pszTable);
1846 0 : if (pszColumn)
1847 0 : CPLFree(pszColumn);
1848 0 : if (pszWhere)
1849 0 : CPLFree(pszWhere);
1850 :
1851 0 : CPLFree(pszConnectionString);
1852 :
1853 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): "
1854 0 : "Opening new dataset: %s", pszFilename);
1855 :
1856 : // connect to the new dataset
1857 0 : GDALOpenInfo poOpenInfo( pszFilename, GA_Update );
1858 : // open the newdataset
1859 0 : poSubDS = (PostGISRasterDataset *)Open(&poOpenInfo);
1860 :
1861 0 : if (poSubDS == NULL) {
1862 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): "
1863 0 : "New dataset could not be opened.");
1864 : }
1865 :
1866 0 : return poSubDS;
1867 : }
1868 :
1869 : /********************************************************
1870 : * \brief Helper method to insert a new raster.
1871 : ********************************************************/
1872 : GBool
1873 0 : PostGISRasterDataset::InsertRaster(PGconn * poConn,
1874 : PostGISRasterDataset * poSrcDS, const char *pszSchema,
1875 : const char * pszTable, const char * pszColumn)
1876 : {
1877 0 : CPLString osCommand;
1878 0 : PGresult * poResult = NULL;
1879 :
1880 0 : if (poSrcDS->pszWhere == NULL) {
1881 : osCommand.Printf("insert into %s.%s (%s) (select %s from %s.%s)",
1882 : pszSchema, pszTable, pszColumn, poSrcDS->pszColumn,
1883 0 : poSrcDS->pszSchema, poSrcDS->pszTable);
1884 : }
1885 : else {
1886 : osCommand.Printf("insert into %s.%s (%s) (select %s from %s.%s where %s)",
1887 : pszSchema, pszTable, pszColumn, poSrcDS->pszColumn,
1888 0 : poSrcDS->pszSchema, poSrcDS->pszTable, poSrcDS->pszWhere);
1889 : }
1890 :
1891 : CPLDebug("PostGIS_Raster", "PostGISRasterDataset::InsertRaster(): Query = %s",
1892 0 : osCommand.c_str());
1893 :
1894 0 : poResult = PQexec(poConn, osCommand.c_str());
1895 0 : if (
1896 : poResult == NULL ||
1897 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
1898 :
1899 : CPLError(CE_Failure, CPLE_AppDefined,
1900 : "Error inserting raster: %s",
1901 0 : PQerrorMessage(poConn));
1902 0 : if (poResult != NULL)
1903 0 : PQclear(poResult);
1904 :
1905 0 : return false;
1906 : }
1907 :
1908 0 : PQclear(poResult);
1909 :
1910 0 : return true;
1911 : }
1912 :
1913 : /*********************************************************
1914 : * \brief Delete a PostGIS Raster dataset.
1915 : *********************************************************/
1916 : CPLErr
1917 0 : PostGISRasterDataset::Delete(const char* pszFilename)
1918 : {
1919 0 : char* pszSchema = NULL;
1920 0 : char* pszTable = NULL;
1921 0 : char* pszColumn = NULL;
1922 0 : char* pszWhere = NULL;
1923 : GBool bBrowseDatabase;
1924 0 : char* pszConnectionString = NULL;
1925 : int nMode;
1926 0 : PGconn * poConn = NULL;
1927 0 : PGresult * poResult = NULL;
1928 0 : CPLString osCommand;
1929 0 : CPLErr nError = CE_Failure;
1930 :
1931 : // Check connection string
1932 0 : if (pszFilename == NULL ||
1933 : !EQUALN(pszFilename, "PG:", 3)) {
1934 : /**
1935 : * The connection string provided is not a valid connection
1936 : * string.
1937 : */
1938 : CPLError( CE_Failure, CPLE_NotSupported,
1939 : "PostGIS Raster driver was unable to parse the provided "
1940 0 : "connection string. Nothing was deleted." );
1941 0 : return CE_Failure;
1942 : }
1943 :
1944 : poConn = GetConnection(pszFilename, &pszConnectionString,
1945 : &pszSchema, &pszTable, &pszColumn, &pszWhere,
1946 0 : &nMode, &bBrowseDatabase);
1947 0 : if (poConn == NULL) {
1948 0 : CPLFree(pszConnectionString);
1949 0 : CPLFree(pszSchema);
1950 0 : CPLFree(pszTable);
1951 0 : CPLFree(pszColumn);
1952 0 : CPLFree(pszWhere);
1953 :
1954 0 : return CE_Failure;
1955 : }
1956 :
1957 : // begin transaction
1958 0 : poResult = PQexec(poConn, "begin");
1959 0 : if (poResult == NULL ||
1960 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
1961 : CPLError(CE_Failure, CPLE_AppDefined,
1962 : "Error beginning database transaction: %s",
1963 0 : PQerrorMessage(poConn));
1964 :
1965 : // set nMode to NO_MODE to avoid any further processing
1966 0 : nMode = NO_MODE;
1967 : }
1968 :
1969 0 : PQclear(poResult);
1970 :
1971 0 : if ( nMode == ONE_RASTER_PER_TABLE ||
1972 : (nMode == ONE_RASTER_PER_ROW && pszWhere == NULL)) {
1973 : // without a where clause, this delete command shall delete
1974 : // all subdatasets, even if the mode is ONE_RASTER_PER_ROW
1975 :
1976 : // drop table <schema>.<table>;
1977 0 : osCommand.Printf("drop table %s.%s", pszSchema, pszTable);
1978 0 : poResult = PQexec(poConn, osCommand.c_str());
1979 0 : if (poResult == NULL ||
1980 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
1981 : CPLError(CE_Failure, CPLE_AppDefined,
1982 : "Couldn't drop the table %s.%s: %s",
1983 0 : pszSchema, pszTable, PQerrorMessage(poConn));
1984 : }
1985 : else {
1986 0 : PQclear(poResult);
1987 0 : nError = CE_None;
1988 : }
1989 : }
1990 0 : else if (nMode == ONE_RASTER_PER_ROW) {
1991 :
1992 : // delete from <schema>.<table> where <where>
1993 : osCommand.Printf("delete from %s.%s where %s", pszSchema,
1994 0 : pszTable, pszWhere);
1995 0 : poResult = PQexec(poConn, osCommand.c_str());
1996 0 : if (poResult == NULL ||
1997 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
1998 : CPLError(CE_Failure, CPLE_AppDefined,
1999 : "Couldn't delete records from the table %s.%s: %s",
2000 0 : pszSchema, pszTable, PQerrorMessage(poConn));
2001 : }
2002 : else {
2003 0 : PQclear(poResult);
2004 0 : nError = CE_None;
2005 : }
2006 : }
2007 :
2008 : // if mode == NO_MODE, the begin transaction above did not complete,
2009 : // so no commit is necessary
2010 0 : if (nMode != NO_MODE) {
2011 0 : poResult = PQexec(poConn, "commit");
2012 0 : if (poResult == NULL ||
2013 : PQresultStatus(poResult) != PGRES_COMMAND_OK) {
2014 : CPLError(CE_Failure, CPLE_AppDefined,
2015 : "Error committing database transaction: %s",
2016 0 : PQerrorMessage(poConn));
2017 :
2018 0 : nError = CE_Failure;
2019 : }
2020 : }
2021 :
2022 0 : if (poResult)
2023 0 : PQclear(poResult);
2024 0 : if (pszSchema)
2025 0 : CPLFree(pszSchema);
2026 0 : if (pszTable)
2027 0 : CPLFree(pszTable);
2028 0 : if (pszColumn)
2029 0 : CPLFree(pszColumn);
2030 0 : if (pszWhere)
2031 0 : CPLFree(pszWhere);
2032 :
2033 : // clean up connection string
2034 0 : CPLFree(pszConnectionString);
2035 :
2036 0 : return nError;
2037 : }
2038 :
2039 : /************************************************************************/
2040 : /* GDALRegister_PostGISRaster() */
2041 :
2042 : /************************************************************************/
2043 582 : void GDALRegister_PostGISRaster() {
2044 : GDALDriver *poDriver;
2045 :
2046 582 : if (GDALGetDriverByName("PostGISRaster") == NULL) {
2047 561 : poDriver = new PostGISRasterDriver();
2048 :
2049 561 : poDriver->SetDescription("PostGISRaster");
2050 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
2051 561 : "PostGIS Raster driver");
2052 :
2053 561 : poDriver->pfnOpen = PostGISRasterDataset::Open;
2054 561 : poDriver->pfnCreateCopy = PostGISRasterDataset::CreateCopy;
2055 561 : poDriver->pfnDelete = PostGISRasterDataset::Delete;
2056 :
2057 561 : GetGDALDriverManager()->RegisterDriver(poDriver);
2058 : }
2059 582 : }
2060 :
|