1 /******************************************************************************
2  *
3  * Project:  GDAL Rasterlite driver
4  * Purpose:  Implement GDAL Rasterlite support using OGR SQLite driver
5  * Author:   Even Rouault, <even dot rouault at spatialys.com>
6  *
7  **********************************************************************
8  * Copyright (c) 2009-2012, Even Rouault <even dot rouault at spatialys.com>
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 #include "cpl_string.h"
30 #include "ogr_api.h"
31 #include "ogr_srs_api.h"
32 
33 #include "rasterlitedataset.h"
34 
35 CPL_CVSID("$Id: rasterlitecreatecopy.cpp 05578039b5938dd4bf099cc17167195a98369c9d 2021-02-27 23:15:12 +0100 Even Rouault $")
36 
37 /************************************************************************/
38 /*                  RasterliteGetTileDriverOptions ()                   */
39 /************************************************************************/
40 
RasterliteAddTileDriverOptionsForDriver(char ** papszOptions,char ** papszTileDriverOptions,const char * pszOptionName,const char * pszExpectedDriverName)41 static char** RasterliteAddTileDriverOptionsForDriver(char** papszOptions,
42                                                     char** papszTileDriverOptions,
43                                                     const char* pszOptionName,
44                                                     const char* pszExpectedDriverName)
45 {
46     const char* pszVal = CSLFetchNameValue(papszOptions, pszOptionName);
47     if (pszVal)
48     {
49         const char* pszDriverName =
50             CSLFetchNameValueDef(papszOptions, "DRIVER", "GTiff");
51         if (EQUAL(pszDriverName, pszExpectedDriverName))
52         {
53             papszTileDriverOptions =
54                 CSLSetNameValue(papszTileDriverOptions, pszOptionName, pszVal);
55         }
56         else
57         {
58             CPLError(CE_Warning, CPLE_NotSupported,
59                      "Unexpected option '%s' for driver '%s'",
60                      pszOptionName, pszDriverName);
61         }
62     }
63     return papszTileDriverOptions;
64 }
65 
RasterliteGetTileDriverOptions(char ** papszOptions)66 char** RasterliteGetTileDriverOptions(char** papszOptions)
67 {
68     const char* pszDriverName =
69         CSLFetchNameValueDef(papszOptions, "DRIVER", "GTiff");
70 
71     char** papszTileDriverOptions = nullptr;
72 
73     const char* pszQuality = CSLFetchNameValue(papszOptions, "QUALITY");
74     if (pszQuality)
75     {
76         if (EQUAL(pszDriverName, "GTiff"))
77         {
78             papszTileDriverOptions =
79                 CSLSetNameValue(papszTileDriverOptions, "JPEG_QUALITY", pszQuality);
80         }
81         else if (EQUAL(pszDriverName, "JPEG") || EQUAL(pszDriverName, "WEBP"))
82         {
83             papszTileDriverOptions =
84                 CSLSetNameValue(papszTileDriverOptions, "QUALITY", pszQuality);
85         }
86         else
87         {
88             CPLError(CE_Warning, CPLE_NotSupported,
89                      "Unexpected option '%s' for driver '%s'",
90                      "QUALITY", pszDriverName);
91         }
92     }
93 
94     papszTileDriverOptions = RasterliteAddTileDriverOptionsForDriver(
95                 papszOptions, papszTileDriverOptions, "COMPRESS", "GTiff");
96     papszTileDriverOptions = RasterliteAddTileDriverOptionsForDriver(
97                 papszOptions, papszTileDriverOptions, "PHOTOMETRIC", "GTiff");
98 
99     return papszTileDriverOptions;
100 }
101 
102 /************************************************************************/
103 /*                      RasterliteInsertSRID ()                         */
104 /************************************************************************/
105 
RasterliteInsertSRID(OGRDataSourceH hDS,const char * pszWKT)106 static int RasterliteInsertSRID(OGRDataSourceH hDS, const char* pszWKT)
107 {
108     int nAuthorityCode = 0;
109     CPLString osAuthorityName, osProjCS, osProj4;
110     if (pszWKT != nullptr && strlen(pszWKT) != 0)
111     {
112         OGRSpatialReferenceH hSRS = OSRNewSpatialReference(pszWKT);
113         if (hSRS)
114         {
115             OSRSetAxisMappingStrategy(hSRS, OAMS_TRADITIONAL_GIS_ORDER);
116 
117             const char* pszAuthorityName = OSRGetAuthorityName(hSRS, nullptr);
118             if (pszAuthorityName) osAuthorityName = pszAuthorityName;
119 
120             const char* pszProjCS = OSRGetAttrValue(hSRS, "PROJCS", 0);
121             if (pszProjCS) osProjCS = pszProjCS;
122 
123             const char* pszAuthorityCode = OSRGetAuthorityCode(hSRS, nullptr);
124             if (pszAuthorityCode) nAuthorityCode = atoi(pszAuthorityCode);
125 
126             char    *pszProj4 = nullptr;
127             if( OSRExportToProj4( hSRS, &pszProj4 ) != OGRERR_NONE )
128             {
129                 CPLFree(pszProj4);
130                 pszProj4 = CPLStrdup("");
131             }
132             osProj4 = pszProj4;
133             CPLFree(pszProj4);
134         }
135         OSRDestroySpatialReference(hSRS);
136     }
137 
138     CPLString osSQL;
139     int nSRSId = -1;
140     if (nAuthorityCode != 0 && !osAuthorityName.empty())
141     {
142         osSQL.Printf   ("SELECT srid FROM spatial_ref_sys WHERE auth_srid = %d", nAuthorityCode);
143         OGRLayerH hLyr = OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), nullptr, nullptr);
144         if (hLyr == nullptr)
145         {
146             nSRSId = nAuthorityCode;
147 
148             if ( !osProjCS.empty() )
149                 osSQL.Printf(
150                     "INSERT INTO spatial_ref_sys "
151                     "(srid, auth_name, auth_srid, ref_sys_name, proj4text) "
152                     "VALUES (%d, '%s', '%d', '%s', '%s')",
153                     nSRSId, osAuthorityName.c_str(),
154                     nAuthorityCode, osProjCS.c_str(), osProj4.c_str() );
155             else
156                 osSQL.Printf(
157                     "INSERT INTO spatial_ref_sys "
158                     "(srid, auth_name, auth_srid, proj4text) "
159                     "VALUES (%d, '%s', '%d', '%s')",
160                     nSRSId, osAuthorityName.c_str(),
161                     nAuthorityCode, osProj4.c_str() );
162 
163             OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), nullptr, nullptr);
164         }
165         else
166         {
167             OGRFeatureH hFeat = OGR_L_GetNextFeature(hLyr);
168             if (hFeat)
169             {
170                 nSRSId = OGR_F_GetFieldAsInteger(hFeat, 0);
171                 OGR_F_Destroy(hFeat);
172             }
173             OGR_DS_ReleaseResultSet(hDS, hLyr);
174         }
175     }
176 
177     return nSRSId;
178 }
179 
180 /************************************************************************/
181 /*                     RasterliteCreateTables ()                        */
182 /************************************************************************/
183 
184 static
RasterliteCreateTables(OGRDataSourceH hDS,const char * pszTableName,int nSRSId,int bWipeExistingData)185 OGRDataSourceH RasterliteCreateTables(OGRDataSourceH hDS, const char* pszTableName,
186                                       int nSRSId, int bWipeExistingData)
187 {
188     CPLString osSQL;
189 
190     const CPLString osDBName = OGR_DS_GetName(hDS);
191 
192     CPLString osRasterLayer;
193     osRasterLayer.Printf("%s_rasters", pszTableName);
194 
195     CPLString osMetadataLayer;
196     osMetadataLayer.Printf("%s_metadata", pszTableName);
197 
198     OGRLayerH hLyr;
199 
200     if (OGR_DS_GetLayerByName(hDS, osRasterLayer.c_str()) == nullptr)
201     {
202 /* -------------------------------------------------------------------- */
203 /*      The table don't exist. Create them                              */
204 /* -------------------------------------------------------------------- */
205 
206         /* Create _rasters table */
207         osSQL.Printf   ("CREATE TABLE \"%s\" ("
208                         "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
209                         "raster BLOB NOT NULL)", osRasterLayer.c_str());
210         OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), nullptr, nullptr);
211 
212         /* Create _metadata table */
213         osSQL.Printf   ("CREATE TABLE \"%s\" ("
214                         "id INTEGER NOT NULL PRIMARY KEY,"
215                         "source_name TEXT NOT NULL,"
216                         "tile_id INTEGER NOT NULL,"
217                         "width INTEGER NOT NULL,"
218                         "height INTEGER NOT NULL,"
219                         "pixel_x_size DOUBLE NOT NULL,"
220                         "pixel_y_size DOUBLE NOT NULL)",
221                         osMetadataLayer.c_str());
222         OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), nullptr, nullptr);
223 
224         /* Add geometry column to _metadata table */
225         osSQL.Printf("SELECT AddGeometryColumn('%s', 'geometry', %d, 'POLYGON', 2)",
226                       osMetadataLayer.c_str(), nSRSId);
227         if ((hLyr = OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), nullptr, nullptr)) == nullptr)
228         {
229             CPLError(CE_Failure, CPLE_AppDefined,
230                      "Check that the OGR SQLite driver has Spatialite support");
231             OGRReleaseDataSource(hDS);
232             return nullptr;
233         }
234         OGR_DS_ReleaseResultSet(hDS, hLyr);
235 
236         /* Create spatial index on _metadata table */
237         osSQL.Printf("SELECT CreateSpatialIndex('%s', 'geometry')",
238                       osMetadataLayer.c_str());
239         if ((hLyr = OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), nullptr, nullptr)) == nullptr)
240         {
241             OGRReleaseDataSource(hDS);
242             return nullptr;
243         }
244         OGR_DS_ReleaseResultSet(hDS, hLyr);
245 
246         /* Create statistics tables */
247         osSQL.Printf("SELECT UpdateLayerStatistics()");
248         CPLPushErrorHandler(CPLQuietErrorHandler);
249         hLyr = OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), nullptr, nullptr);
250         CPLPopErrorHandler();
251         OGR_DS_ReleaseResultSet(hDS, hLyr);
252 
253         /* Re-open the DB to take into account the new tables*/
254         OGRReleaseDataSource(hDS);
255 
256         hDS = RasterliteOpenSQLiteDB(osDBName.c_str(), GA_Update);
257     }
258     else
259     {
260         /* Check that the existing SRS is consistent with the one of the new */
261         /* data to be inserted */
262         osSQL.Printf("SELECT srid FROM geometry_columns WHERE f_table_name = '%s'",
263                      osMetadataLayer.c_str());
264         hLyr = OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), nullptr, nullptr);
265         if (hLyr)
266         {
267             int nExistingSRID = -1;
268             OGRFeatureH hFeat = OGR_L_GetNextFeature(hLyr);
269             if (hFeat)
270             {
271                 nExistingSRID = OGR_F_GetFieldAsInteger(hFeat, 0);
272                 OGR_F_Destroy(hFeat);
273             }
274             OGR_DS_ReleaseResultSet(hDS, hLyr);
275 
276             if (nExistingSRID != nSRSId)
277             {
278                 if (bWipeExistingData)
279                 {
280                     osSQL.Printf("UPDATE geometry_columns SET srid = %d "
281                                  "WHERE f_table_name = \"%s\"",
282                                  nSRSId, osMetadataLayer.c_str());
283                     OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), nullptr, nullptr);
284 
285                     /* Re-open the DB to take into account the change of SRS */
286                     OGRReleaseDataSource(hDS);
287 
288                     hDS = RasterliteOpenSQLiteDB(osDBName.c_str(), GA_Update);
289                 }
290                 else
291                 {
292                     CPLError(CE_Failure, CPLE_NotSupported,
293                              "New data has not the same SRS as existing data");
294                     OGRReleaseDataSource(hDS);
295                     return nullptr;
296                 }
297             }
298         }
299 
300         if (bWipeExistingData)
301         {
302             osSQL.Printf("DELETE FROM \"%s\"", osRasterLayer.c_str());
303             OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), nullptr, nullptr);
304 
305             osSQL.Printf("DELETE FROM \"%s\"", osMetadataLayer.c_str());
306             OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), nullptr, nullptr);
307         }
308     }
309 
310     return hDS;
311 }
312 
313 /************************************************************************/
314 /*                       RasterliteCreateCopy ()                        */
315 /************************************************************************/
316 
317 GDALDataset *
RasterliteCreateCopy(const char * pszFilename,GDALDataset * poSrcDS,CPL_UNUSED int bStrict,char ** papszOptions,GDALProgressFunc pfnProgress,void * pProgressData)318 RasterliteCreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
319                       CPL_UNUSED int bStrict,
320                       char ** papszOptions,
321                       GDALProgressFunc pfnProgress, void * pProgressData )
322 {
323     const int nBands = poSrcDS->GetRasterCount();
324     if (nBands == 0)
325     {
326         CPLError(CE_Failure, CPLE_NotSupported, "nBands == 0");
327         return nullptr;
328     }
329 
330     const char* pszDriverName = CSLFetchNameValueDef(papszOptions, "DRIVER", "GTiff");
331     if (EQUAL(pszDriverName, "MEM") || EQUAL(pszDriverName, "VRT"))
332     {
333         CPLError(CE_Failure, CPLE_AppDefined, "GDAL %s driver cannot be used as underlying driver",
334                  pszDriverName);
335         return nullptr;
336     }
337 
338     GDALDriverH hTileDriver = GDALGetDriverByName(pszDriverName);
339     if ( hTileDriver == nullptr)
340     {
341         CPLError(CE_Failure, CPLE_AppDefined, "Cannot load GDAL %s driver", pszDriverName);
342         return nullptr;
343     }
344 
345     GDALDriverH hMemDriver = GDALGetDriverByName("MEM");
346     if (hMemDriver == nullptr)
347     {
348         CPLError(CE_Failure, CPLE_AppDefined, "Cannot load GDAL MEM driver");
349         return nullptr;
350     }
351 
352     const int nXSize = GDALGetRasterXSize(poSrcDS);
353     const int nYSize = GDALGetRasterYSize(poSrcDS);
354 
355     double adfGeoTransform[6];
356     if (poSrcDS->GetGeoTransform(adfGeoTransform) != CE_None)
357     {
358         adfGeoTransform[0] = 0;
359         adfGeoTransform[1] = 1;
360         adfGeoTransform[2] = 0;
361         adfGeoTransform[3] = 0;
362         adfGeoTransform[4] = 0;
363         adfGeoTransform[5] = -1;
364     }
365     else if (adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0)
366     {
367         CPLError(CE_Failure, CPLE_AppDefined, "Cannot use geotransform with rotational terms");
368         return nullptr;
369     }
370 
371     const bool bTiled = CPLTestBool(CSLFetchNameValueDef(papszOptions, "TILED", "YES"));
372     int nBlockXSize, nBlockYSize;
373     if (bTiled)
374     {
375         nBlockXSize = atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "256"));
376         nBlockYSize = atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "256"));
377         if (nBlockXSize < 64) nBlockXSize = 64;
378         else if (nBlockXSize > 4096)  nBlockXSize = 4096;
379         if (nBlockYSize < 64) nBlockYSize = 64;
380         else if (nBlockYSize > 4096)  nBlockYSize = 4096;
381     }
382     else
383     {
384         nBlockXSize = nXSize;
385         nBlockYSize = nYSize;
386     }
387 
388 /* -------------------------------------------------------------------- */
389 /*      Analyze arguments                                               */
390 /* -------------------------------------------------------------------- */
391 
392     /* Skip optional RASTERLITE: prefix */
393     const char* pszFilenameWithoutPrefix = pszFilename;
394     if (STARTS_WITH_CI(pszFilename, "RASTERLITE:"))
395         pszFilenameWithoutPrefix += 11;
396 
397     char** papszTokens = CSLTokenizeStringComplex(
398                 pszFilenameWithoutPrefix, ",", FALSE, FALSE );
399     const int nTokens = CSLCount(papszTokens);
400     CPLString osDBName;
401     CPLString osTableName;
402     if (nTokens == 0)
403     {
404         osDBName = pszFilenameWithoutPrefix;
405         osTableName = CPLGetBasename(pszFilenameWithoutPrefix);
406     }
407     else
408     {
409         osDBName = papszTokens[0];
410 
411         int i;
412         for(i=1;i<nTokens;i++)
413         {
414             if (STARTS_WITH_CI(papszTokens[i], "table="))
415                 osTableName = papszTokens[i] + 6;
416             else
417             {
418                 CPLError(CE_Warning, CPLE_AppDefined,
419                          "Invalid option : %s", papszTokens[i]);
420             }
421         }
422     }
423 
424     CSLDestroy(papszTokens);
425     papszTokens = nullptr;
426 
427     VSIStatBuf sBuf;
428     const bool bExists = (VSIStat(osDBName.c_str(), &sBuf) == 0);
429 
430     if (osTableName.empty())
431     {
432         if (bExists)
433         {
434             CPLError(CE_Failure, CPLE_AppDefined,
435                      "Database already exists. Explicit table name must be specified");
436             return nullptr;
437         }
438         osTableName = CPLGetBasename(osDBName.c_str());
439     }
440 
441     CPLString osRasterLayer;
442     osRasterLayer.Printf("%s_rasters", osTableName.c_str());
443 
444     CPLString osMetadataLayer;
445     osMetadataLayer.Printf("%s_metadata", osTableName.c_str());
446 
447 /* -------------------------------------------------------------------- */
448 /*      Create or open the SQLite DB                                    */
449 /* -------------------------------------------------------------------- */
450 
451     if (OGRGetDriverCount() == 0)
452         OGRRegisterAll();
453 
454     OGRSFDriverH hSQLiteDriver = OGRGetDriverByName("SQLite");
455     if (hSQLiteDriver == nullptr)
456     {
457         CPLError(CE_Failure, CPLE_AppDefined, "Cannot load OGR SQLite driver");
458         return nullptr;
459     }
460 
461     OGRDataSourceH hDS;
462 
463     if (!bExists)
464     {
465         char** papszOGROptions = CSLAddString(nullptr, "SPATIALITE=YES");
466         hDS = OGR_Dr_CreateDataSource(hSQLiteDriver,
467                                       osDBName.c_str(), papszOGROptions);
468         CSLDestroy(papszOGROptions);
469     }
470     else
471     {
472         hDS = RasterliteOpenSQLiteDB(osDBName.c_str(), GA_Update);
473     }
474 
475     if (hDS == nullptr)
476     {
477         CPLError(CE_Failure, CPLE_AppDefined,
478                  "Cannot load or create SQLite database");
479         return nullptr;
480     }
481 
482     CPLString osSQL;
483 
484 /* -------------------------------------------------------------------- */
485 /*      Get the SRID for the SRS                                        */
486 /* -------------------------------------------------------------------- */
487     int nSRSId = RasterliteInsertSRID(hDS, poSrcDS->GetProjectionRef());
488 
489 /* -------------------------------------------------------------------- */
490 /*      Create or wipe existing tables                                  */
491 /* -------------------------------------------------------------------- */
492     const int bWipeExistingData =
493         CPLTestBool(CSLFetchNameValueDef(papszOptions, "WIPE", "NO"));
494 
495     hDS = RasterliteCreateTables(hDS, osTableName.c_str(),
496                                  nSRSId, bWipeExistingData);
497     if (hDS == nullptr)
498         return nullptr;
499 
500     OGRLayerH hRasterLayer = OGR_DS_GetLayerByName(hDS, osRasterLayer.c_str());
501     OGRLayerH hMetadataLayer = OGR_DS_GetLayerByName(hDS, osMetadataLayer.c_str());
502     if (hRasterLayer == nullptr || hMetadataLayer == nullptr)
503     {
504         CPLError(CE_Failure, CPLE_AppDefined,
505                  "Cannot find metadata and/or raster tables");
506         OGRReleaseDataSource(hDS);
507         return nullptr;
508     }
509 
510 /* -------------------------------------------------------------------- */
511 /*      Check if there is overlapping data and warn the user            */
512 /* -------------------------------------------------------------------- */
513     double minx = adfGeoTransform[0];
514     double maxx = adfGeoTransform[0] + nXSize * adfGeoTransform[1];
515     double maxy = adfGeoTransform[3];
516     double miny = adfGeoTransform[3] + nYSize * adfGeoTransform[5];
517 
518     osSQL.Printf("SELECT COUNT(geometry) FROM \"%s\" "
519                  "WHERE rowid IN "
520                  "(SELECT pkid FROM \"idx_%s_metadata_geometry\" "
521                   "WHERE %s) AND %s",
522                   osMetadataLayer.c_str(),
523                   osTableName.c_str(),
524                   RasterliteGetSpatialFilterCond(minx, miny, maxx, maxy).c_str(),
525                   RasterliteGetPixelSizeCond(adfGeoTransform[1], -adfGeoTransform[5]).c_str());
526 
527     int nOverlappingGeoms = 0;
528     OGRLayerH hCountLyr = OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), nullptr, nullptr);
529     if (hCountLyr)
530     {
531         OGRFeatureH hFeat = OGR_L_GetNextFeature(hCountLyr);
532         if (hFeat)
533         {
534             nOverlappingGeoms = OGR_F_GetFieldAsInteger(hFeat, 0);
535             OGR_F_Destroy(hFeat);
536         }
537         OGR_DS_ReleaseResultSet(hDS, hCountLyr);
538     }
539 
540     if (nOverlappingGeoms != 0)
541     {
542         CPLError(CE_Warning, CPLE_AppDefined,
543                  "Raster tiles already exist in the %s table within "
544                  "the extent of the data to be inserted in",
545                  osTableName.c_str());
546     }
547 
548 /* -------------------------------------------------------------------- */
549 /*      Iterate over blocks to add data into raster and metadata tables */
550 /* -------------------------------------------------------------------- */
551     int nXBlocks = (nXSize + nBlockXSize - 1) / nBlockXSize;
552     int nYBlocks = (nYSize + nBlockYSize - 1) / nBlockYSize;
553 
554     GDALDataType eDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
555     int nDataTypeSize = GDALGetDataTypeSize(eDataType) / 8;
556     GByte* pabyMEMDSBuffer =
557         reinterpret_cast<GByte *>(
558             VSIMalloc3(nBlockXSize, nBlockYSize, nBands * nDataTypeSize) );
559     if (pabyMEMDSBuffer == nullptr)
560     {
561         OGRReleaseDataSource(hDS);
562         return nullptr;
563     }
564 
565     CPLString osTempFileName;
566     osTempFileName.Printf("/vsimem/%p", hDS);
567 
568     int nTileId = 0;
569     int nBlocks = 0;
570     int nTotalBlocks = nXBlocks * nYBlocks;
571 
572     char** papszTileDriverOptions = RasterliteGetTileDriverOptions(papszOptions);
573 
574     OGR_DS_ExecuteSQL(hDS, "BEGIN", nullptr, nullptr);
575 
576     CPLErr eErr = CE_None;
577     for(int nBlockYOff=0;eErr == CE_None && nBlockYOff<nYBlocks;nBlockYOff++)
578     {
579         for(int nBlockXOff=0;eErr == CE_None && nBlockXOff<nXBlocks;nBlockXOff++)
580         {
581 /* -------------------------------------------------------------------- */
582 /*      Create in-memory tile                                           */
583 /* -------------------------------------------------------------------- */
584             int nReqXSize = nBlockXSize;
585             int nReqYSize = nBlockYSize;
586             if ((nBlockXOff+1) * nBlockXSize > nXSize)
587                 nReqXSize = nXSize - nBlockXOff * nBlockXSize;
588             if ((nBlockYOff+1) * nBlockYSize > nYSize)
589                 nReqYSize = nYSize - nBlockYOff * nBlockYSize;
590 
591             eErr = poSrcDS->RasterIO(GF_Read,
592                                      nBlockXOff * nBlockXSize,
593                                      nBlockYOff * nBlockYSize,
594                                      nReqXSize, nReqYSize,
595                                      pabyMEMDSBuffer, nReqXSize, nReqYSize,
596                                      eDataType, nBands, nullptr,
597                                      0, 0, 0, nullptr);
598             if (eErr != CE_None)
599             {
600                 break;
601             }
602 
603             GDALDatasetH hMemDS = GDALCreate(hMemDriver, "MEM:::",
604                                               nReqXSize, nReqYSize, 0,
605                                               eDataType, nullptr);
606             if (hMemDS == nullptr)
607             {
608                 eErr = CE_Failure;
609                 break;
610             }
611 
612             for( int iBand = 0; iBand < nBands; iBand ++)
613             {
614                 char szTmp[64];
615                 memset(szTmp, 0, sizeof(szTmp));
616                 CPLPrintPointer(szTmp,
617                                 pabyMEMDSBuffer + iBand * nDataTypeSize *
618                                 nReqXSize * nReqYSize, sizeof(szTmp));
619                 char** papszMEMDSOptions
620                     = CSLSetNameValue(nullptr, "DATAPOINTER", szTmp);
621                 GDALAddBand(hMemDS, eDataType, papszMEMDSOptions);
622                 CSLDestroy(papszMEMDSOptions);
623             }
624 
625             GDALDatasetH hOutDS = GDALCreateCopy(hTileDriver,
626                                         osTempFileName.c_str(), hMemDS, FALSE,
627                                         papszTileDriverOptions, nullptr, nullptr);
628 
629             GDALClose(hMemDS);
630             if ( !hOutDS )
631             {
632                 eErr = CE_Failure;
633                 break;
634             }
635             GDALClose(hOutDS);
636 
637 /* -------------------------------------------------------------------- */
638 /*      Insert new entry into raster table                              */
639 /* -------------------------------------------------------------------- */
640             vsi_l_offset nDataLength = 0;
641             GByte *pabyData = VSIGetMemFileBuffer( osTempFileName.c_str(),
642                                                    &nDataLength, FALSE);
643 
644             OGRFeatureH hFeat = OGR_F_Create( OGR_L_GetLayerDefn(hRasterLayer) );
645             OGR_F_SetFieldBinary(
646                 hFeat, 0, static_cast<int>( nDataLength ), pabyData);
647 
648             if( OGR_L_CreateFeature(hRasterLayer, hFeat) != OGRERR_NONE )
649                 eErr = CE_Failure;
650             /* Query raster ID to set it as the ID of the associated metadata */
651             int nRasterID = static_cast<int>( OGR_F_GetFID( hFeat ) );
652 
653             OGR_F_Destroy(hFeat);
654 
655             VSIUnlink(osTempFileName.c_str());
656             if( eErr == CE_Failure )
657                 break;
658 
659 /* -------------------------------------------------------------------- */
660 /*      Insert new entry into metadata table                            */
661 /* -------------------------------------------------------------------- */
662 
663             hFeat = OGR_F_Create( OGR_L_GetLayerDefn(hMetadataLayer) );
664             OGR_F_SetFID(hFeat, nRasterID);
665             OGR_F_SetFieldString(hFeat, 0, GDALGetDescription(poSrcDS));
666             OGR_F_SetFieldInteger(hFeat, 1, nTileId ++);
667             OGR_F_SetFieldInteger(hFeat, 2, nReqXSize);
668             OGR_F_SetFieldInteger(hFeat, 3, nReqYSize);
669             OGR_F_SetFieldDouble(hFeat, 4, adfGeoTransform[1]);
670             OGR_F_SetFieldDouble(hFeat, 5, -adfGeoTransform[5]);
671 
672             minx = adfGeoTransform[0] +
673                 (nBlockXSize * nBlockXOff) * adfGeoTransform[1];
674             maxx = adfGeoTransform[0] +
675                 (nBlockXSize * nBlockXOff + nReqXSize) * adfGeoTransform[1];
676             maxy = adfGeoTransform[3] +
677                 (nBlockYSize * nBlockYOff) * adfGeoTransform[5];
678             miny = adfGeoTransform[3] +
679                 (nBlockYSize * nBlockYOff + nReqYSize) * adfGeoTransform[5];
680 
681             OGRGeometryH hRectangle = OGR_G_CreateGeometry(wkbPolygon);
682             OGRGeometryH hLinearRing = OGR_G_CreateGeometry(wkbLinearRing);
683             OGR_G_AddPoint_2D(hLinearRing, minx, miny);
684             OGR_G_AddPoint_2D(hLinearRing, minx, maxy);
685             OGR_G_AddPoint_2D(hLinearRing, maxx, maxy);
686             OGR_G_AddPoint_2D(hLinearRing, maxx, miny);
687             OGR_G_AddPoint_2D(hLinearRing, minx, miny);
688             OGR_G_AddGeometryDirectly(hRectangle, hLinearRing);
689 
690             OGR_F_SetGeometryDirectly(hFeat, hRectangle);
691 
692             if( OGR_L_CreateFeature(hMetadataLayer, hFeat) != OGRERR_NONE )
693                 eErr = CE_Failure;
694             OGR_F_Destroy(hFeat);
695 
696             nBlocks++;
697             if (pfnProgress && !pfnProgress(1.0 * nBlocks / nTotalBlocks,
698                                             nullptr, pProgressData))
699                 eErr = CE_Failure;
700         }
701     }
702 
703     VSIUnlink(osTempFileName);
704     VSIUnlink((osTempFileName + ".aux.xml").c_str());
705 
706     if (eErr == CE_None)
707         OGR_DS_ExecuteSQL(hDS, "COMMIT", nullptr, nullptr);
708     else
709         OGR_DS_ExecuteSQL(hDS, "ROLLBACK", nullptr, nullptr);
710 
711     CSLDestroy(papszTileDriverOptions);
712 
713     VSIFree(pabyMEMDSBuffer);
714 
715     OGRReleaseDataSource(hDS);
716 
717     if( eErr == CE_Failure )
718         return nullptr;
719 
720     return reinterpret_cast<GDALDataset *>(
721         GDALOpen( pszFilename, GA_Update ) );
722 }
723 
724 /************************************************************************/
725 /*                         RasterliteDelete ()                          */
726 /************************************************************************/
727 
RasterliteDelete(CPL_UNUSED const char * pszFilename)728 CPLErr RasterliteDelete(CPL_UNUSED const char* pszFilename)
729 {
730     return CE_None;
731 }
732