1 : /******************************************************************************
2 : * $Id: rasdamandataset.cpp 23362 2011-11-11 11:11:52Z rouault $
3 : * Project: rasdaman Driver
4 : * Purpose: Implement Rasdaman GDAL driver
5 : * Author: Constantin Jucovschi, jucovschi@yahoo.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2010, Constantin Jucovschi
9 : *
10 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ******************************************************************************/
28 :
29 :
30 : #include "gdal_pam.h"
31 : #include "cpl_string.h"
32 : #include "regex.h"
33 : #include <string>
34 : #include <memory>
35 :
36 :
37 : #define __EXECUTABLE__
38 : #define EARLY_TEMPLATE
39 : #include "raslib/template_inst.hh"
40 : #include "raslib/structuretype.hh"
41 : #include "raslib/type.hh"
42 :
43 : #include "rasodmg/database.hh"
44 :
45 : CPL_CVSID("$Id: rasdamandataset.cpp 23362 2011-11-11 11:11:52Z rouault $");
46 :
47 :
48 : CPL_C_START
49 : void GDALRegister_RASDAMAN(void);
50 : CPL_C_END
51 :
52 :
53 : /************************************************************************/
54 : /* ==================================================================== */
55 : /* RasdamanDataset */
56 : /* ==================================================================== */
57 : /************************************************************************/
58 :
59 :
60 : class RasdamanRasterBand;
61 : static CPLString getQuery(const char *templateString, const char* x_lo, const char* x_hi, const char* y_lo, const char* y_hi);
62 :
63 : class RasdamanDataset : public GDALPamDataset
64 2 : {
65 : friend class RasdamanRasterBand;
66 :
67 : public:
68 : ~RasdamanDataset();
69 :
70 : static GDALDataset *Open( GDALOpenInfo * );
71 :
72 : private:
73 : void getTypes(const r_Base_Type* baseType, int &counter, int pos);
74 : void createBands(const char* queryString);
75 :
76 : CPLString queryParam;
77 : CPLString host;
78 : int port;
79 : CPLString username;
80 : CPLString userpassword;
81 : CPLString databasename;
82 : int xPos;
83 : int yPos;
84 : int tileXSize;
85 : int tileYSize;
86 : };
87 :
88 : /************************************************************************/
89 : /* ==================================================================== */
90 : /* RasdamanRasterBand */
91 : /* ==================================================================== */
92 : /************************************************************************/
93 :
94 : class RasdamanRasterBand : public GDALPamRasterBand
95 : {
96 : friend class RasdamanDataset;
97 :
98 : int nRecordSize;
99 : int typeOffset;
100 : int typeSize;
101 :
102 : public:
103 :
104 : RasdamanRasterBand( RasdamanDataset *, int, GDALDataType type, int offset, int size, int nBlockXSize, int nBlockYSize );
105 : ~RasdamanRasterBand();
106 :
107 : virtual CPLErr IReadBlock( int, int, void * );
108 : };
109 :
110 :
111 : /************************************************************************/
112 : /* RasdamanRasterBand() */
113 : /************************************************************************/
114 :
115 0 : RasdamanRasterBand::RasdamanRasterBand( RasdamanDataset *poDS, int nBand, GDALDataType type, int offset, int size, int nBlockXSize, int nBlockYSize )
116 : {
117 0 : this->poDS = poDS;
118 0 : this->nBand = nBand;
119 :
120 0 : eDataType = type;
121 0 : typeSize = size;
122 0 : typeOffset = offset;
123 :
124 0 : this->nBlockXSize = nBlockXSize;
125 0 : this->nBlockYSize = nBlockYSize;
126 :
127 0 : nRecordSize = nBlockXSize * nBlockYSize * typeSize;
128 0 : }
129 :
130 : /************************************************************************/
131 : /* ~RasdamanRasterBand() */
132 : /************************************************************************/
133 :
134 0 : RasdamanRasterBand::~RasdamanRasterBand()
135 : {
136 :
137 0 : }
138 :
139 : /************************************************************************/
140 : /* IReadBlock() */
141 : /************************************************************************/
142 :
143 0 : CPLErr RasdamanRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
144 : void * pImage )
145 :
146 : {
147 : //cerr << "Read block " << nBlockXOff << " " << nBlockYOff << endl;
148 0 : RasdamanDataset *poGDS = (RasdamanDataset *) poDS;
149 :
150 0 : r_Database database;
151 0 : r_Transaction transaction;
152 :
153 0 : memset(pImage, 0, nRecordSize);
154 :
155 : try {
156 0 : database.set_servername(poGDS->host, poGDS->port);
157 0 : database.set_useridentification(poGDS->username, poGDS->userpassword);
158 0 : database.open(poGDS->databasename);
159 0 : transaction.begin();
160 :
161 : char x_lo[11], x_hi[11], y_lo[11], y_hi[11];
162 0 : int xPos = poGDS->xPos;
163 0 : int yPos = poGDS->yPos;
164 :
165 0 : sprintf(x_lo, "%d", nBlockXOff * nBlockXSize);
166 0 : sprintf(x_hi, "%d", (nBlockXOff+1) * nBlockXSize-1);
167 0 : sprintf(y_lo, "%d", nBlockYOff * nBlockYSize);
168 0 : sprintf(y_hi, "%d", (nBlockYOff+1) * nBlockYSize-1);
169 0 : CPLString queryString = getQuery(poGDS->queryParam, x_lo, x_hi, y_lo, y_hi);
170 :
171 0 : r_Set<r_Ref_Any> result_set;
172 0 : r_OQL_Query query (queryString);
173 0 : r_oql_execute (query, result_set);
174 0 : if (result_set.get_element_type_schema()->type_id() == r_Type::MARRAYTYPE) {
175 0 : r_Iterator<r_Ref_Any> iter = result_set.create_iterator();
176 0 : r_Ref<r_GMarray> gmdd = r_Ref<r_GMarray>(*iter);
177 0 : r_Minterval sp = gmdd->spatial_domain();
178 0 : r_Point extent = sp.get_extent();
179 0 : r_Point base = sp.get_origin();
180 0 : int tileX = extent[xPos];
181 0 : int tileY = extent[yPos];
182 0 : r_Point access = base;
183 : char *resultPtr;
184 0 : for (int j=0; j<tileY; ++j) {
185 0 : for (int i=0; i<tileX; ++i) {
186 0 : resultPtr = (char*)pImage + (j*nBlockYSize+i)*typeSize;
187 0 : access[xPos] = base[xPos]+i;
188 0 : access[yPos] = base[yPos]+j;
189 0 : const char *data = (*gmdd)[access] + typeOffset;
190 0 : memcpy(resultPtr, data, typeSize);
191 : }
192 0 : }
193 : }
194 :
195 0 : transaction.commit();
196 0 : database.close();
197 0 : } catch (r_Error error) {
198 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", error.what());
199 0 : return CPLGetLastErrorType();
200 : }
201 0 : return CE_None;
202 : }
203 :
204 : /************************************************************************/
205 : /* ==================================================================== */
206 : /* RasdamanDataset */
207 : /* ==================================================================== */
208 : /************************************************************************/
209 :
210 : /************************************************************************/
211 : /* ~RasdamanDataset() */
212 : /************************************************************************/
213 :
214 2 : RasdamanDataset::~RasdamanDataset()
215 : {
216 2 : FlushCache();
217 2 : }
218 :
219 : /************************************************************************/
220 : /* GetProjectionRef() */
221 : /************************************************************************/
222 :
223 14 : static CPLString getOption(const char *string, regmatch_t cMatch, const char* defaultValue) {
224 14 : if (cMatch.rm_eo == -1 || cMatch.rm_so == -1)
225 8 : return defaultValue;
226 6 : char *result = new char[cMatch.rm_eo-cMatch.rm_so+1];
227 6 : strncpy(result, string+cMatch.rm_so, cMatch.rm_eo-cMatch.rm_so);
228 6 : result[cMatch.rm_eo-cMatch.rm_so]=0;
229 6 : CPLString osResult = result;
230 6 : delete[] result;
231 6 : return osResult;
232 : }
233 :
234 6 : static int getOption(const char *string, regmatch_t cMatch, int defaultValue) {
235 6 : if (cMatch.rm_eo == -1 || cMatch.rm_so == -1)
236 6 : return defaultValue;
237 0 : char *result = new char[cMatch.rm_eo-cMatch.rm_so+1];
238 0 : strncpy(result, string+cMatch.rm_so, cMatch.rm_eo-cMatch.rm_so);
239 0 : result[cMatch.rm_eo-cMatch.rm_so]=0;
240 0 : int nRet = atoi(result);
241 0 : delete[] result;
242 0 : return nRet;
243 : }
244 :
245 6 : static CPLString getQuery(const char *templateString, const char* x_lo, const char* x_hi, const char* y_lo, const char* y_hi) {
246 : static regex_t* replaceRegEx = NULL;
247 : regmatch_t match[3];
248 6 : if (replaceRegEx == NULL) {
249 2 : replaceRegEx = new regex_t;
250 2 : regcomp(replaceRegEx, "\\$(x|y)_(lo|hi)", REG_EXTENDED);
251 : }
252 :
253 6 : int pos = 0, resPos = 0;
254 6 : char *result = new char[strlen(templateString)*2];
255 :
256 36 : while (regexec(replaceRegEx, templateString+pos, 3, match, 0) == 0) {
257 24 : strncpy(result+resPos, templateString+pos, match[1].rm_so-1);
258 24 : resPos += match[1].rm_so-1;
259 24 : if (templateString[pos+match[1].rm_so]=='x') {
260 12 : if (templateString[pos+match[2].rm_so]=='h') {
261 6 : strcpy(result+resPos, x_hi); resPos+=strlen(x_hi);
262 : } else {
263 6 : strcpy(result+resPos, x_lo); resPos+=strlen(x_lo);
264 : }
265 : } else {
266 12 : if (templateString[pos+match[2].rm_so]=='h') {
267 6 : strcpy(result+resPos, y_hi); resPos+=strlen(y_hi);
268 : } else {
269 6 : strcpy(result+resPos, y_lo); resPos+=strlen(y_lo);
270 : }
271 : }
272 24 : pos += match[2].rm_eo;
273 : }
274 6 : int rest = strlen(templateString+pos);
275 6 : strncpy(result+resPos, templateString+pos, rest);
276 6 : resPos += rest;
277 6 : result[resPos]='\0';
278 6 : CPLString osResult = result;
279 6 : delete[] result;
280 : return osResult;
281 : }
282 :
283 0 : GDALDataType mapRasdamanTypesToGDAL(r_Type::r_Type_Id typeId) {
284 0 : switch (typeId) {
285 : case r_Type::ULONG:
286 0 : return GDT_UInt32;
287 : case r_Type::LONG:
288 0 : return GDT_Int32;
289 : case r_Type::SHORT:
290 0 : return GDT_Int16;
291 : case r_Type::USHORT:
292 0 : return GDT_UInt16;
293 : case r_Type::BOOL:
294 : case r_Type::CHAR:
295 0 : return GDT_Byte;
296 : case r_Type::DOUBLE:
297 0 : return GDT_Float64;
298 : case r_Type::FLOAT:
299 0 : return GDT_Float32;
300 : case r_Type::COMPLEXTYPE1:
301 0 : return GDT_CFloat32;
302 : case r_Type::COMPLEXTYPE2:
303 0 : return GDT_CFloat64;
304 : default:
305 0 : return GDT_Unknown;
306 : }
307 : }
308 :
309 0 : void RasdamanDataset::getTypes(const r_Base_Type* baseType, int &counter, int pos) {
310 0 : if (baseType->isStructType()) {
311 0 : r_Structure_Type* tp = (r_Structure_Type*) baseType;
312 0 : int elem = tp->count_elements();
313 0 : for (int i=0; i<elem; ++i) {
314 0 : r_Attribute attr = (*tp)[i];
315 0 : getTypes(&attr.type_of(), counter, attr.global_offset());
316 : }
317 :
318 : }
319 0 : if (baseType->isPrimitiveType()) {
320 0 : r_Primitive_Type *primType = (r_Primitive_Type*)baseType;
321 0 : r_Type::r_Type_Id typeId = primType->type_id();
322 0 : SetBand(counter, new RasdamanRasterBand(this, counter, mapRasdamanTypesToGDAL(typeId), pos, primType->size(), this->tileXSize, this->tileYSize));
323 0 : counter ++;
324 : }
325 0 : }
326 :
327 0 : void RasdamanDataset::createBands(const char* queryString) {
328 0 : r_Set<r_Ref_Any> result_set;
329 0 : r_OQL_Query query (queryString);
330 0 : r_oql_execute (query, result_set);
331 0 : if (result_set.get_element_type_schema()->type_id() == r_Type::MARRAYTYPE) {
332 0 : r_Iterator<r_Ref_Any> iter = result_set.create_iterator();
333 0 : r_Ref<r_GMarray> gmdd = r_Ref<r_GMarray>(*iter);
334 0 : const r_Base_Type* baseType = gmdd->get_base_type_schema();
335 0 : int counter = 1;
336 0 : getTypes(baseType, counter, 0);
337 0 : }
338 0 : }
339 :
340 :
341 0 : static int getExtent(const char *queryString, int &pos) {
342 0 : r_Set<r_Ref_Any> result_set;
343 0 : r_OQL_Query query (queryString);
344 0 : r_oql_execute (query, result_set);
345 0 : if (result_set.get_element_type_schema()->type_id() == r_Type::MINTERVALTYPE) {
346 0 : r_Iterator<r_Ref_Any> iter = result_set.create_iterator();
347 0 : r_Ref<r_Minterval> interv = r_Ref<r_Minterval>(*iter);
348 0 : r_Point extent = interv->get_extent();
349 0 : int dim = extent.dimension();
350 0 : int result = -1;
351 0 : for (int i=0; i<dim; ++i) {
352 0 : if (extent[i] == 1)
353 0 : continue;
354 0 : if (result != -1)
355 0 : return -1;
356 0 : result = extent[i];
357 0 : pos = i;
358 : }
359 0 : if (result == -1)
360 0 : return 1;
361 : else
362 0 : return result;
363 : } else
364 0 : return -1;
365 : }
366 :
367 :
368 : /************************************************************************/
369 : /* Open() */
370 : /************************************************************************/
371 :
372 25918 : GDALDataset *RasdamanDataset::Open( GDALOpenInfo * poOpenInfo )
373 : {
374 : // buffer to communicate errors
375 : char errbuffer[4096];
376 :
377 : // fast checks if current module should handle the request
378 : // check 1: the request is not on a existing file in the file system
379 25918 : if (poOpenInfo->fp != NULL) {
380 3856 : return NULL;
381 : }
382 : // check 2: the request contains --collection
383 22062 : char* connString = poOpenInfo->pszFilename;
384 22062 : if (!EQUALN(connString, "rasdaman", 8)) {
385 22060 : return NULL;
386 : }
387 :
388 : // regex for parsing options
389 : regex_t optionRegEx;
390 : // regex for parsing query
391 : regex_t queryRegEx;
392 : // array to store matching subexpressions
393 : regmatch_t matches[10];
394 :
395 : #define QUERY_POSITION 2
396 : #define SERVER_POSITION 3
397 : #define PORT_POSITION 4
398 : #define USERNAME_POSITION 5
399 : #define USERPASSWORD_POSITION 6
400 : #define DATABASE_POSITION 7
401 : #define TILEXSIZE_POSITION 8
402 : #define TILEYSIZE_POSITION 9
403 :
404 2 : int result = regcomp(&optionRegEx, "^rasdaman:(query='([[:alnum:][:punct:] ]+)'|host='([[:alnum:]]+)'|port=([0-9]+)|user='([[:alnum:]]+)'|password='([[:alnum:]]+)'|database '([[:alnum:]]+)'|tileXSize=([0-9]+)|tileYSize=([0-9]+)| )*", REG_EXTENDED);
405 :
406 : // should never happen
407 2 : if (result != 0) {
408 0 : regerror(result, &optionRegEx, errbuffer, 4096);
409 0 : CPLError(CE_Failure, CPLE_AppDefined, "Internal error at compiling option parsing regex: %s", errbuffer);
410 0 : return NULL;
411 : }
412 :
413 2 : result = regcomp(&queryRegEx, "^select ([[:alnum:][:punct:] ]*) from ([[:alnum:][:punct:] ]*)$", REG_EXTENDED);
414 : // should never happen
415 2 : if (result != 0) {
416 0 : regerror(result, &queryRegEx, errbuffer, 4096);
417 0 : CPLError(CE_Failure, CPLE_AppDefined, "Internal error at compiling option parsing regex: %s", errbuffer);
418 0 : return NULL;
419 : }
420 :
421 : // executing option parsing regex on the connection string and checking if it succeeds
422 2 : result = regexec(&optionRegEx, connString, 10, matches, 0);
423 2 : if (result != 0) {
424 0 : regerror(result, &optionRegEx, errbuffer, 4096);
425 0 : CPLError(CE_Failure, CPLE_AppDefined, "Parsing opening parameters failed with error: %s", errbuffer);
426 0 : regfree(&optionRegEx);
427 0 : regfree(&queryRegEx);
428 0 : return NULL;
429 : }
430 :
431 2 : regfree(&optionRegEx);
432 :
433 :
434 : // checking if the whole expressions was matches, if not give an error where
435 : // the matching stopped and exit
436 2 : if (size_t(matches[0].rm_eo) < strlen(connString)) {
437 0 : CPLError(CE_Failure, CPLE_AppDefined, "Parsing opening parameters failed with error: %s", connString+matches[0].rm_eo);
438 0 : regfree(&queryRegEx);
439 0 : return NULL;
440 : }
441 :
442 2 : CPLString queryParam = getOption(connString, matches[QUERY_POSITION], (const char*)NULL);
443 2 : CPLString host = getOption(connString, matches[SERVER_POSITION], "localhost");
444 2 : int port = getOption(connString, matches[PORT_POSITION], 7001);
445 2 : CPLString username = getOption(connString, matches[USERNAME_POSITION], "rasguest");
446 2 : CPLString userpassword = getOption(connString, matches[USERPASSWORD_POSITION], "rasguest");
447 2 : CPLString databasename = getOption(connString, matches[DATABASE_POSITION], "RASBASE");
448 2 : int tileXSize = getOption(connString, matches[TILEXSIZE_POSITION], 1024);
449 2 : int tileYSize = getOption(connString, matches[TILEYSIZE_POSITION], 1024);
450 :
451 2 : result = regexec(&queryRegEx, queryParam, 10, matches, 0);
452 2 : if (result != 0) {
453 0 : regerror(result, &queryRegEx, errbuffer, 4096);
454 0 : CPLError(CE_Failure, CPLE_AppDefined, "Parsing query parameter failed with error: %s", errbuffer);
455 0 : regfree(&queryRegEx);
456 0 : return NULL;
457 : }
458 :
459 2 : regfree(&queryRegEx);
460 :
461 2 : CPLString osQueryString = "select sdom(";
462 2 : osQueryString += getOption(queryParam, matches[1], "");
463 2 : osQueryString += ") from ";
464 2 : osQueryString += getOption(queryParam, matches[2], "");
465 :
466 2 : CPLString queryX = getQuery(osQueryString, "*", "*", "0", "0");
467 2 : CPLString queryY = getQuery(osQueryString, "0", "0", "*", "*");
468 2 : CPLString queryUnit = getQuery(queryParam, "0", "0", "0", "0");
469 :
470 2 : stringstream queryStream;
471 :
472 2 : r_Transaction transaction;
473 :
474 2 : RasdamanDataset *rasDataset = new RasdamanDataset();
475 :
476 2 : r_Database database;
477 : try {
478 2 : database.set_servername(host, port);
479 2 : database.set_useridentification(username, userpassword);
480 2 : database.open(databasename);
481 0 : transaction.begin();
482 :
483 0 : int dimX = getExtent(queryX, rasDataset->xPos);
484 0 : int dimY = getExtent(queryY, rasDataset->yPos);
485 0 : rasDataset->nRasterXSize = dimX;
486 0 : rasDataset->nRasterYSize = dimY;
487 0 : rasDataset->tileXSize = tileXSize;
488 0 : rasDataset->tileYSize = tileYSize;
489 0 : rasDataset->createBands(queryUnit);
490 :
491 0 : transaction.commit();
492 0 : database.close();
493 4 : } catch (r_Error error) {
494 2 : CPLError(CE_Failure, CPLE_AppDefined, "%s", error.what());
495 2 : delete rasDataset;
496 2 : return NULL;
497 : }
498 0 : rasDataset->queryParam = queryParam;
499 0 : rasDataset->host = host;
500 0 : rasDataset->port = port;
501 0 : rasDataset->username = username;
502 0 : rasDataset->userpassword = userpassword;
503 0 : rasDataset->databasename = databasename;
504 :
505 0 : return rasDataset;
506 : }
507 :
508 : /************************************************************************/
509 : /* GDALRegister_RASDAMAN() */
510 : /************************************************************************/
511 :
512 1135 : extern void GDALRegister_RASDAMAN()
513 :
514 : {
515 : GDALDriver *poDriver;
516 :
517 1135 : if( GDALGetDriverByName( "RASDAMAN" ) == NULL )
518 : {
519 1093 : poDriver = new GDALDriver();
520 :
521 1093 : poDriver->SetDescription( "RASDAMAN" );
522 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
523 1093 : "RASDAMAN" );
524 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
525 1093 : "frmt_rasdaman.html" );
526 :
527 1093 : poDriver->pfnOpen = RasdamanDataset::Open;
528 :
529 1093 : GetGDALDriverManager()->RegisterDriver( poDriver );
530 : }
531 5164 : }
|