EVOLUTION-MANAGER
Edit File: ogrdb2datasource.cpp
/**************************************************************************** * * Project: DB2 Spatial driver * Purpose: Implements OGRDB2DataSource class * Author: David Adler, dadler at adtechgeospatial dot com * **************************************************************************** * Copyright (c) 2010, Tamas Szekeres * Copyright (c) 2015, David Adler * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************/ #include "ogr_db2.h" CPL_CVSID("$Id: ogrdb2datasource.cpp 36682 2016-12-04 20:34:45Z rouault $"); static GPKGTileFormat GetTileFormat(const char* pszTF ); /* layer status */ #define DB2LAYERSTATUS_ORIGINAL 0 #define DB2LAYERSTATUS_INITIAL 1 #define DB2LAYERSTATUS_CREATED 2 #define DB2LAYERSTATUS_DISABLED 3 /************************************************************************/ /* Tiling schemes */ /************************************************************************/ typedef struct { const char* pszName; int nEPSGCode; double dfMinX; double dfMaxY; int nTileXCountZoomLevel0; int nTileYCountZoomLevel0; int nTileWidth; int nTileHeight; double dfPixelXSizeZoomLevel0; double dfPixelYSizeZoomLevel0; } TilingSchemeDefinition; static const TilingSchemeDefinition asTilingShemes[] = { /* See http://portal.opengeospatial.org/files/?artifact_id=35326 (WMTS 1.0), Annex E.3 */ { "GoogleCRS84Quad", 4326, -180.0, 180.0, 1, 1, 256, 256, 360.0 / 256, 360.0 / 256 }, /* See http://portal.opengeospatial.org/files/?artifact_id=35326 (WMTS 1.0), Annex E.4 */ { "GoogleMapsCompatible", 3857, -(156543.0339280410*256) /2, (156543.0339280410*256) /2, 1, 1, 256, 256, 156543.0339280410, 156543.0339280410 }, /* See InspireCRS84Quad at http://inspire.ec.europa.eu/documents/Network_Services/TechnicalGuidance_ViewServices_v3.0.pdf */ /* This is exactly the same as PseudoTMS_GlobalGeodetic */ { "InspireCRS84Quad", 4326, -180.0, 90.0, 2, 1, 256, 256, 180.0 / 256, 180.0 / 256 }, /* See global-geodetic at http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification */ { "PseudoTMS_GlobalGeodetic", 4326, -180.0, 90.0, 2, 1, 256, 256, 180.0 / 256, 180.0 / 256 }, /* See global-mercator at http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification */ { "PseudoTMS_GlobalMercator", 3857, -20037508.34, 20037508.34, 2, 2, 256, 256, 78271.516, 78271.516 }, }; /************************************************************************/ /* OGRDB2DataSource() */ /************************************************************************/ OGRDB2DataSource::OGRDB2DataSource() { clock1 = clock(); time1 = time(NULL); m_bIsVector = TRUE; m_papszTableNames = NULL; m_papszSchemaNames = NULL; m_papszGeomColumnNames = NULL; m_papszCoordDimensions = NULL; m_papszSRIds = NULL; m_papszSRTexts = NULL; m_pszName = NULL; m_pszCatalog = NULL; m_bHasMetadataTables = FALSE; m_nKnownSRID = 0; m_panSRID = NULL; m_papoSRS = NULL; bUseGeometryColumns = CPLTestBool(CPLGetConfigOption( "DB2SPATIAL_USE_GEOMETRY_COLUMNS", "YES")); bListAllTables = CPLTestBool(CPLGetConfigOption( "DB2SPATIAL_LIST_ALL_TABLES", "NO")); // from GPKG m_bUpdate = FALSE; m_bNew = FALSE; m_papoLayers = NULL; m_nLayers = 0; m_bUtf8 = FALSE; m_bIdentifierAsCO = FALSE; m_bDescriptionAsCO = FALSE; m_bHasReadMetadataFromStorage = FALSE; m_bMetadataDirty = FALSE; m_papszSubDatasets = NULL; m_pszProjection = NULL; m_bRecordInsertedInGPKGContent = FALSE; m_bGeoTransformValid = FALSE; m_nSRID = -1; /* unknown cartesian */ m_adfGeoTransform[0] = 0.0; m_adfGeoTransform[1] = 1.0; m_adfGeoTransform[2] = 0.0; m_adfGeoTransform[3] = 0.0; m_adfGeoTransform[4] = 0.0; m_adfGeoTransform[5] = 1.0; m_nZoomLevel = -1; m_pabyCachedTiles = NULL; for(int i=0; i<4; i++) { m_asCachedTilesDesc[i].nRow = -1; m_asCachedTilesDesc[i].nCol = -1; m_asCachedTilesDesc[i].nIdxWithinTileData = -1; m_asCachedTilesDesc[i].abBandDirty[0] = FALSE; m_asCachedTilesDesc[i].abBandDirty[1] = FALSE; m_asCachedTilesDesc[i].abBandDirty[2] = FALSE; m_asCachedTilesDesc[i].abBandDirty[3] = FALSE; } m_nShiftXTiles = 0; m_nShiftXPixelsMod = 0; m_nShiftYTiles = 0; m_nShiftYPixelsMod = 0; m_eTF = GPKG_TF_PNG_JPEG; m_nTileMatrixWidth = 0; m_nTileMatrixHeight = 0; m_nZLevel = 6; m_nQuality = 75; m_bDither = FALSE; m_poParentDS = NULL; m_nOverviewCount = 0; m_papoOverviewDS = NULL; m_bZoomOther = FALSE; m_bTriedEstablishingCT = FALSE; m_pabyHugeColorArray = NULL; m_poCT = NULL; m_bInWriteTile = FALSE; //m_hTempDB = NULL; m_bInFlushCache = FALSE; m_nTileInsertionCount = 0; m_osTilingScheme = "CUSTOM"; } /************************************************************************/ /* ~OGRDB2DataSource() */ /************************************************************************/ OGRDB2DataSource::~OGRDB2DataSource() { DB2_DEBUG_ENTER("OGRDB2DataSource::~OGRDB2DataSource"); int i; SetPamFlags(0); CPLFree( m_pszName ); CPLFree( m_pszCatalog ); CSLDestroy( m_papszTableNames ); CSLDestroy( m_papszSchemaNames ); CSLDestroy( m_papszGeomColumnNames ); CSLDestroy( m_papszCoordDimensions ); CSLDestroy( m_papszSRIds ); CSLDestroy( m_papszSRTexts ); if (!m_bIsVector) { if( m_poParentDS == NULL && !m_osRasterTable.empty() && !m_bGeoTransformValid ) { CPLError(CE_Failure, CPLE_AppDefined, "Raster table %s not correctly initialized due to missing call " "to SetGeoTransform()", m_osRasterTable.c_str()); } FlushCache(); FlushMetadata(); } for( i = 0; i < m_nLayers; i++ ) delete m_papoLayers[i]; CPLFree( m_papoLayers ); for( i = 0; i < m_nKnownSRID; i++ ) { if( m_papoSRS[i] != NULL ) CPLDebug("OGRDB2DataSource::~OGRDB2DataSource","m_papoSRS[%d] is not null", i); //LATER m_papoSRS[i]->Release(); //fails for some reason } CPLFree( m_panSRID ); CPLFree( m_papoSRS ); DB2_DEBUG_EXIT("OGRDB2DataSource::~OGRDB2DataSource"); } /*======================================================================*/ /* */ /* getDTime() */ /* */ /* Return the time since last called. */ /*======================================================================*/ double OGRDB2DataSource::getDTime() { clock2 = clock(); time2 = time(NULL); dclock = (clock2 - clock1) / CLOCKS_PER_SEC; dtime = (double)(time2 - time1 ); clock1 = clock2; time1 = time2; strcpy(stime, ctime(&time2)); /* get current time as a string */ stime[strlen(stime) - 1] = '\0'; /* get rid of newline */ CPLDebug("getDTime","stime: '%s', dclock: %f, dtime: %f", stime, dclock, dtime); return dclock; } /************************************************************************/ /* TestCapability() */ /************************************************************************/ int OGRDB2DataSource::TestCapability( const char * pszCap ) { if( EQUAL(pszCap,ODsCCreateLayer) || EQUAL(pszCap,ODsCDeleteLayer) ) return TRUE; else if( EQUAL(pszCap,ODsCRandomLayerWrite) ) return TRUE; else return FALSE; } /************************************************************************/ /* GetLayer() */ /************************************************************************/ OGRLayer *OGRDB2DataSource::GetLayer( int iLayer ) { CPLDebug("OGR_DB2DataSource::GetLayer", "pszLayer %d", iLayer); if( iLayer < 0 || iLayer >= m_nLayers ) return NULL; else return m_papoLayers[iLayer]; } /************************************************************************/ /* GetLayerByName() */ /************************************************************************/ // Layer names are always uppercased - for now OGRLayer *OGRDB2DataSource::GetLayerByName( const char* pszLayerName ) { if (!pszLayerName) return NULL; char *pszTableName = NULL; char *pszSchemaName = NULL; OGRLayer *poLayer = NULL; char *pszLayerNameUpper = NULL; CPLDebug("OGR_DB2DataSource::GetLayerByName", "pszLayerName: '%s'", pszLayerName); pszLayerNameUpper = ToUpper(pszLayerName); const char* pszDotPos = strstr(pszLayerNameUpper,"."); if ( pszDotPos != NULL ) { int length = static_cast<int>(pszDotPos - pszLayerNameUpper); pszSchemaName = (char*)CPLMalloc(length+1); strncpy(pszSchemaName, pszLayerNameUpper, length); pszSchemaName[length] = '\0'; pszTableName = CPLStrdup( pszDotPos + 1 ); //skip "." } else { pszTableName = CPLStrdup( pszLayerNameUpper ); } for( int iLayer = 0; iLayer < m_nLayers; iLayer++ ) { if( EQUAL(pszTableName,m_papoLayers[iLayer]->GetTableName()) && (pszSchemaName == NULL || EQUAL(pszSchemaName,m_papoLayers[iLayer]->GetSchemaName()))) { CPLDebug("OGR_DB2DataSource::GetLayerByName", "found layer: %d; schema: '%s'; table: '%s'", iLayer,m_papoLayers[iLayer]->GetSchemaName(), m_papoLayers[iLayer]->GetTableName()); poLayer = m_papoLayers[iLayer]; } } CPLFree( pszSchemaName ); CPLFree( pszTableName ); CPLFree( pszLayerNameUpper ); return poLayer; } /************************************************************************/ /* DeleteLayer(OGRDB2TableLayer * poLayer) */ /************************************************************************/ int OGRDB2DataSource::DeleteLayer( OGRDB2TableLayer * poLayer ) { int iLayer = 0; if( poLayer == NULL ) return OGRERR_FAILURE; /* -------------------------------------------------------------------- */ /* Blow away our OGR structures related to the layer. This is */ /* pretty dangerous if anything has a reference to this layer! */ /* -------------------------------------------------------------------- */ const char* pszTableName = poLayer->GetTableName(); const char* pszSchemaName = poLayer->GetSchemaName(); OGRDB2Statement oStatement( &m_oSession ); oStatement.Appendf("DROP TABLE %s.%s", pszSchemaName, pszTableName ); CPLDebug( "OGR_DB2DataSource::DeleteLayer", "Drop stmt: '%s'", oStatement.GetCommand()); for( iLayer = 0; iLayer < m_nLayers; iLayer++ ) { if (poLayer == m_papoLayers[iLayer]) break; } delete m_papoLayers[iLayer]; // free the layer object // move remaining layers down memmove( m_papoLayers + iLayer, m_papoLayers + iLayer + 1, sizeof(void *) * (m_nLayers - iLayer - 1) ); m_nLayers--; /* -------------------------------------------------------------------- */ /* Remove from the database. */ /* -------------------------------------------------------------------- */ m_oSession.BeginTransaction(); if( !oStatement.DB2Execute("OGR_DB2DataSource::DeleteLayer") ) { CPLError( CE_Failure, CPLE_AppDefined, "Error deleting layer: %s", GetSession()->GetLastError() ); return OGRERR_FAILURE; } m_oSession.CommitTransaction(); return OGRERR_NONE; } /************************************************************************/ /* DeleteLayer(int iLayer) */ /************************************************************************/ int OGRDB2DataSource::DeleteLayer( int iLayer ) { if( iLayer < 0 || iLayer >= m_nLayers ) return OGRERR_FAILURE; return DeleteLayer(m_papoLayers[iLayer]); } /************************************************************************/ /* ICreateLayer() */ /************************************************************************/ OGRLayer * OGRDB2DataSource::ICreateLayer( const char * pszLayerName, OGRSpatialReference *poSRS, OGRwkbGeometryType eType, char ** papszOptions ) { char *pszTableName = NULL; char *pszSchemaName = NULL; const char *pszGeomColumn = NULL; int nCoordDimension = 3; CPLDebug("OGR_DB2DataSource::ICreateLayer", "layer name: %s",pszLayerName); CSLPrint(papszOptions, stderr); /* determine the dimension */ if( eType == wkbFlatten(eType) ) nCoordDimension = 2; if( CSLFetchNameValue( papszOptions, "DIM") != NULL ) nCoordDimension = atoi(CSLFetchNameValue( papszOptions, "DIM")); /* DB2 Schema handling: Extract schema name from input layer name or passed with -lco SCHEMA. Set layer name to "schema.table" or to "table" if schema is not specified */ const char* pszDotPos = strstr(pszLayerName,"."); if ( pszDotPos != NULL ) { int length = static_cast<int>(pszDotPos - pszLayerName); pszSchemaName = (char*)CPLMalloc(length+1); strncpy(pszSchemaName, pszLayerName, length); pszSchemaName[length] = '\0'; /* For now, always convert layer name to uppercase table name*/ pszTableName = ToUpper( pszDotPos + 1 ); } else { pszSchemaName = NULL; /* For now, always convert layer name to uppercase table name*/ pszTableName = ToUpper( pszLayerName ); } if( CSLFetchNameValue( papszOptions, "SCHEMA" ) != NULL ) { CPLFree(pszSchemaName); pszSchemaName = CPLStrdup(CSLFetchNameValue(papszOptions, "SCHEMA")); } /* -------------------------------------------------------------------- */ /* Do we already have this layer? If so, should we blow it */ /* away? */ /* -------------------------------------------------------------------- */ int iLayer; for( iLayer = 0; iLayer < m_nLayers; iLayer++ ) { CPLDebug("OGR_DB2DataSource::ICreateLayer", "schema: '%s'; table: '%s'", m_papoLayers[iLayer]->GetSchemaName(), m_papoLayers[iLayer]->GetTableName()); if( EQUAL(pszTableName,m_papoLayers[iLayer]->GetTableName()) && (pszSchemaName == NULL || EQUAL(pszSchemaName,m_papoLayers[iLayer]->GetSchemaName())) ) { CPLDebug("OGR_DB2DataSource::ICreateLayer", "Found match, schema: '%s'; table: '%s'" , pszSchemaName, pszTableName); if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != NULL && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"), "NO")) { if (!pszSchemaName) pszSchemaName = CPLStrdup(m_papoLayers[iLayer]-> GetSchemaName()); DeleteLayer( iLayer ); } else { CPLError( CE_Failure, CPLE_AppDefined, "Layer %s already exists, CreateLayer failed.\n" "Use the layer creation option OVERWRITE=YES to " "replace it.", pszLayerName ); CPLFree( pszSchemaName ); CPLFree( pszTableName ); return NULL; } } } /* determine the geometry column name */ pszGeomColumn = CSLFetchNameValue( papszOptions, "GEOM_NAME"); if (!pszGeomColumn) pszGeomColumn = "OGR_geometry"; /* -------------------------------------------------------------------- */ /* Try to get the SRS Id of this spatial reference system, */ /* adding to the srs table if needed. */ /* -------------------------------------------------------------------- */ int nSRSId = 0; if( CSLFetchNameValue( papszOptions, "SRID") != NULL ) nSRSId = atoi(CSLFetchNameValue( papszOptions, "SRID")); if( nSRSId == 0 && poSRS != NULL ) nSRSId = FetchSRSId( poSRS ); OGRDB2Statement oStatement( &m_oSession ); if (pszSchemaName != NULL) oStatement.Appendf("CREATE TABLE %s.%s ", pszSchemaName, pszTableName); else oStatement.Appendf("CREATE TABLE %s" , pszTableName); oStatement.Appendf(" (ogr_fid int not null " // "primary key , " "primary key GENERATED BY DEFAULT AS IDENTITY, " "%s db2gse.st_%s )", pszGeomColumn, OGRToOGCGeomType(eType)); m_oSession.BeginTransaction(); if( !oStatement.DB2Execute("OGR_DB2DataSource::ICreateLayer") ) { CPLError( CE_Failure, CPLE_AppDefined, "Error creating layer: %s", GetSession()->GetLastError() ); CPLDebug("OGR_DB2DataSource::ICreateLayer", "create failed"); return NULL; } m_oSession.CommitTransaction(); // If we didn't have a schema name when the table was created, // get the schema that was created by implicit if (pszSchemaName == NULL) { oStatement.Clear(); oStatement.Appendf( "SELECT table_schema FROM db2gse.st_geometry_columns " "WHERE table_name = '%s'", pszTableName); if( oStatement.DB2Execute("OGR_DB2DataSource::ICreateLayer") && oStatement.Fetch()) { pszSchemaName = CPLStrdup( oStatement.GetColData(0) ); } } /* -------------------------------------------------------------------- */ /* Create the layer object. */ /* -------------------------------------------------------------------- */ OGRDB2TableLayer *poLayer = new OGRDB2TableLayer( this ); poLayer->SetLaunderFlag( CPLFetchBool(papszOptions, "LAUNDER", true) ); poLayer->SetPrecisionFlag( CPLFetchBool(papszOptions, "PRECISION", true)); char *pszWKT = NULL; if( poSRS && poSRS->exportToWkt( &pszWKT ) != OGRERR_NONE ) { CPLFree(pszWKT); pszWKT = NULL; } CPLDebug("OGR_DB2DataSource::ICreateLayer", "srs wkt: %s",pszWKT); if (poLayer->Initialize(pszSchemaName, pszTableName, pszGeomColumn, nCoordDimension, nSRSId, pszWKT, eType) == OGRERR_FAILURE) { CPLFree( pszSchemaName ); CPLFree( pszTableName ); CPLFree( pszWKT ); return NULL; } CPLFree( pszSchemaName ); CPLFree( pszTableName ); CPLFree( pszWKT ); /* -------------------------------------------------------------------- */ /* Add layer to data source layer list. */ /* -------------------------------------------------------------------- */ m_papoLayers = (OGRDB2TableLayer **) CPLRealloc( m_papoLayers, sizeof(OGRDB2TableLayer *) * (m_nLayers+1)); m_papoLayers[m_nLayers++] = poLayer; return poLayer; } /************************************************************************/ /* OpenTable() */ /************************************************************************/ int OGRDB2DataSource::OpenTable( const char *pszSchemaName, const char *pszTableName, const char *pszGeomCol, int nCoordDimension, int nSRID, const char *pszSRText, OGRwkbGeometryType eType) { /* -------------------------------------------------------------------- */ /* Create the layer object. */ /* -------------------------------------------------------------------- */ OGRDB2TableLayer *poLayer = new OGRDB2TableLayer( this ); CPLDebug( "OGR_DB2DataSource::OpenTable", "pszSchemaName: '%s'; pszTableName: '%s'; pszGeomCol: '%s'", pszSchemaName , pszTableName, pszGeomCol); if( poLayer->Initialize( pszSchemaName, pszTableName, pszGeomCol, nCoordDimension, nSRID, pszSRText, eType ) ) { delete poLayer; return FALSE; } /* -------------------------------------------------------------------- */ /* Add layer to data source layer list. */ /* -------------------------------------------------------------------- */ m_papoLayers = (OGRDB2TableLayer **) CPLRealloc( m_papoLayers, sizeof(OGRDB2TableLayer *) * (m_nLayers+1) ); m_papoLayers[m_nLayers++] = poLayer; return TRUE; } /************************************************************************/ /* GetLayerCount() */ /************************************************************************/ int OGRDB2DataSource::GetLayerCount() { return m_nLayers; } /************************************************************************/ /* ParseValue() */ /************************************************************************/ int OGRDB2DataSource::ParseValue(char** pszValue, char* pszSource, const char* pszKey, int nStart, int nNext, int nTerm, int bRemove) { int nLen = static_cast<int>(strlen(pszKey)); if ((*pszValue) == NULL && nStart + nLen < nNext && EQUALN(pszSource + nStart, pszKey, nLen)) { *pszValue = (char*)CPLMalloc( sizeof(char) * (nNext - nStart - nLen + 1) ); if (*pszValue) strncpy(*pszValue, pszSource + nStart + nLen, nNext - nStart - nLen); (*pszValue)[nNext - nStart - nLen] = 0; if (bRemove) { // remove the value from the source string if (pszSource[nNext] == ';') memmove( pszSource + nStart, pszSource + nNext + 1, nTerm - nNext); else memmove( pszSource + nStart, pszSource + nNext, nTerm - nNext + 1); } return TRUE; } return FALSE; } /************************************************************************/ /* Create() */ /************************************************************************/ int OGRDB2DataSource::Create( const char * pszFilename, int nXSize, int nYSize, int nBandsIn, GDALDataType eDT, char **papszOptions ) { CPLString osCommand; /* First, ensure there isn't any such file yet. */ VSIStatBufL sStatBuf; CPLDebug( "OGR_DB2DataSource::Create", "pszFileName: '%s'", pszFilename); CPLDebug( "OGR_DB2DataSource::Create", "(%s,%s,%d,%d,%d,%s,%p)", GetDescription(), pszFilename, nXSize, nYSize, nBandsIn, GDALGetDataTypeName( eDT ), papszOptions ); if (!InitializeSession(pszFilename, 0)) { CPLDebug( "OGR_DB2DataSource::Create", "Session initialization failed"); return FALSE; } if (!HasMetadataTables()) { CPLDebug( "OGR_DB2DataSource::Create", "No metadata tables and create failed"); return FALSE; } if( nBandsIn != 0 ) { if( eDT != GDT_Byte ) { CPLError(CE_Failure, CPLE_NotSupported, "Only Byte supported"); return FALSE; } if( nBandsIn != 1 && nBandsIn != 2 && nBandsIn != 3 && nBandsIn != 4 ) { CPLError(CE_Failure, CPLE_NotSupported, "Only 1 (Grey/ColorTable), 2 (Grey+Alpha), 3 (RGB) or 4 (RGBA) band dataset supported"); return FALSE; } } //int bFileExists = FALSE; if( VSIStatL( pszFilename, &sStatBuf ) == 0 ) { //bFileExists = TRUE; if( nBandsIn == 0 || !CPLTestBool(CSLFetchNameValueDef(papszOptions, "APPEND_SUBDATASET", "NO")) ) { CPLError( CE_Failure, CPLE_AppDefined, "A file system object called '%s' already exists.", pszFilename ); return FALSE; } } m_bIsVector = FALSE; m_pszFilename = CPLStrdup(pszFilename); m_bNew = true; m_bUpdate = TRUE; eAccess = GA_Update; /* hum annoying duplication */ if( nBandsIn != 0 ) { CPLDebug( "OGR_DB2DataSource::Create", "pszFileName: '%s'", pszFilename); m_osRasterTable = CSLFetchNameValueDef(papszOptions, "RASTER_TABLE", "RASTERTABLE"); m_bIdentifierAsCO = CSLFetchNameValue(papszOptions, "RASTER_IDENTIFIER" ) != NULL; m_osIdentifier = CSLFetchNameValueDef(papszOptions, "RASTER_IDENTIFIER", m_osRasterTable); m_bDescriptionAsCO = CSLFetchNameValue(papszOptions, "RASTER_DESCRIPTION" ) != NULL; m_osDescription = CSLFetchNameValueDef(papszOptions, "RASTER_DESCRIPTION", ""); CPLDebug("OGR_DB2DataSource::Create", "m_osRasterTable: '%s'", m_osRasterTable.c_str()); CPLDebug("OGR_DB2DataSource::Create", "m_osIdentifier: '%s'", m_osIdentifier.c_str()); CPLDebug("OGR_DB2DataSource::Create", "m_osDescription: '%s'", m_osDescription.c_str()); m_oSession.BeginTransaction(); OGRDB2Statement oStatement( &m_oSession ); // Drop the table with raster data oStatement.Appendf("DROP TABLE %s", m_osRasterTable.c_str()); if( !oStatement.DB2Execute("OGR_DB2DataSource::Create") ) { CPLDebug("OGR_DB2DataSource::Create", "DROP failed: %s", GetSession()->GetLastError() ); } oStatement.Clear(); oStatement.Appendf("CREATE TABLE %s" , m_osRasterTable.c_str()); oStatement.Appendf("(" "id INTEGER NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY," "zoom_level INTEGER NOT NULL," "tile_column INTEGER NOT NULL," "tile_row INTEGER NOT NULL," "tile_data BLOB NOT NULL," "UNIQUE (zoom_level, tile_column, tile_row)" ")"); if( !oStatement.DB2Execute("OGR_DB2DataSource::Create") ) { CPLError( CE_Failure, CPLE_AppDefined, "Error creating layer: %s", GetSession()->GetLastError() ); CPLDebug("OGR_DB2DataSource::Create", "create failed"); return FALSE; } // Remove entries from raster catalog tables - will cascade oStatement.Clear(); oStatement.Appendf("DELETE FROM gpkg.contents WHERE table_name = '%s'", m_osRasterTable.c_str()); if( !oStatement.DB2Execute("OGR_DB2DataSource::Create") ) { CPLDebug("OGR_DB2DataSource::Create", "DELETE failed: %s", GetSession()->GetLastError() ); } m_oSession.CommitTransaction(); nRasterXSize = nXSize; nRasterYSize = nYSize; const char* pszTileSize = CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", "256"); const char* pszTileWidth = CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", pszTileSize); const char* pszTileHeight = CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", pszTileSize); int nTileWidth = atoi(pszTileWidth); int nTileHeight = atoi(pszTileHeight); if( (nTileWidth < 8 || nTileWidth > 4096 || nTileHeight < 8 || nTileHeight > 4096) && !CPLTestBool(CPLGetConfigOption("GPKG_ALLOW_CRAZY_SETTINGS", "NO")) ) { CPLError(CE_Failure, CPLE_AppDefined, "Invalid block dimensions: %dx%d", nTileWidth, nTileHeight); return FALSE; } m_pabyCachedTiles = (GByte*) VSI_MALLOC3_VERBOSE(4 * 4, nTileWidth, nTileHeight); if( m_pabyCachedTiles == NULL ) { return FALSE; } for(int i = 1; i <= nBandsIn; i ++) SetBand( i, new GDALDB2RasterBand(this, i, nTileWidth, nTileHeight) ); CPLDebug("OGR_DB2DataSource::Create","setting metadata PIXEL"); GDALPamDataset::SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE"); GDALPamDataset::SetMetadataItem("IDENTIFIER", m_osIdentifier); if( !m_osDescription.empty() ) GDALPamDataset::SetMetadataItem("DESCRIPTION", m_osDescription); const char* pszTF = CSLFetchNameValue(papszOptions, "TILE_FORMAT"); if( pszTF ) m_eTF = GetTileFormat(pszTF); ParseCompressionOptions(papszOptions); if( m_eTF == GPKG_TF_WEBP ) { if( !RegisterWebPExtension() ) return FALSE; } const char* pszTilingScheme = CSLFetchNameValue(papszOptions, "TILING_SCHEME"); if( pszTilingScheme ) { m_osTilingScheme = pszTilingScheme; int bFound = FALSE; for(size_t iScheme = 0; iScheme < sizeof(asTilingShemes)/sizeof(asTilingShemes[0]); iScheme++ ) { if( EQUAL(m_osTilingScheme, asTilingShemes[iScheme].pszName) ) { if( nTileWidth != asTilingShemes[iScheme].nTileWidth || nTileHeight != asTilingShemes[iScheme].nTileHeight ) { CPLError(CE_Failure, CPLE_NotSupported, "Tile dimension should be %dx%d for %s tiling scheme", asTilingShemes[iScheme].nTileWidth, asTilingShemes[iScheme].nTileHeight, m_osTilingScheme.c_str()); return FALSE; } /* Implicitly sets SRS */ OGRSpatialReference oSRS; if( oSRS.importFromEPSG(asTilingShemes[iScheme].nEPSGCode) != OGRERR_NONE ) return FALSE; char* pszWKT = NULL; oSRS.exportToWkt(&pszWKT); SetProjection(pszWKT); CPLFree(pszWKT); bFound = TRUE; break; } } if( !bFound ) m_osTilingScheme = "CUSTOM"; } } CPLDebug("OGR_DB2DataSource::Create","exiting"); return TRUE; } /************************************************************************/ /* Open() */ /************************************************************************/ int OGRDB2DataSource::Open( GDALOpenInfo* poOpenInfo ) { int bRet = FALSE; SetDescription( poOpenInfo->pszFilename ); #ifdef DEBUG_DB2 CPLDebug("OGR_DB2DataSource::OpenNew", "papszOpenOptions"); CSLPrint((char **) poOpenInfo->papszOpenOptions, stderr); #endif CPLString osFilename( poOpenInfo->pszFilename ); CPLString osSubdatasetTableName; m_bUpdate = poOpenInfo->eAccess == GA_Update; CPLDebug( "OGR_DB2DataSource::OpenNew", "pszFileName: '%s'; m_bUpdate: %d, eAccess: %d; GA_Update: %d", poOpenInfo->pszFilename, m_bUpdate, poOpenInfo->eAccess, GA_Update); eAccess = poOpenInfo->eAccess; /* hum annoying duplication */ m_pszFilename = CPLStrdup( osFilename ); if( poOpenInfo->nOpenFlags & GDAL_OF_VECTOR ) { CPLDebug( "OGR_DB2DataSource::OpenNew", "Open vector"); return Open(poOpenInfo->pszFilename, 0); } if( poOpenInfo->nOpenFlags & GDAL_OF_RASTER ) { CPLDebug( "OGR_DB2DataSource::OpenNew", "Open raster"); m_bIsVector = FALSE; if (!InitializeSession(poOpenInfo->pszFilename, 0)) { CPLDebug( "OGR_DB2DataSource::Open", "Session initialization failed"); return FALSE; } OGRDB2Statement oStatement( GetSession() ); oStatement.Appendf( "SELECT c.table_name, c.identifier, c.description, c.srs_id, c.min_x, c.min_y, c.max_x, c.max_y, " "tms.min_x, tms.min_y, tms.max_x, tms.max_y FROM gpkg.contents c JOIN gpkg.tile_matrix_set tms ON " "c.table_name = tms.table_name WHERE data_type = 'tiles'"); if( CSLFetchNameValue( poOpenInfo->papszOpenOptions, "TABLE") ) { osSubdatasetTableName = CSLFetchNameValue( poOpenInfo->papszOpenOptions, "TABLE"); } if( !osSubdatasetTableName.empty() ) { oStatement.Appendf(" AND c.table_name='%s'", osSubdatasetTableName.c_str()); SetPhysicalFilename( osFilename.c_str() ); //LATER } if( !oStatement.DB2Execute("OGR_DB2DataSource::OpenNew") ) { CPLError(CE_Failure, CPLE_AppDefined, "Raster query failed: %s", GetSession()->GetLastError()); return FALSE; } if( oStatement.Fetch() ) { CPLDebug("OGRDB2DataSource::OpenNew", "Raster table_name: %s", oStatement.GetColData(0)); const char *pszTableName = oStatement.GetColData(0); const char* pszIdentifier = oStatement.GetColData(1); const char* pszDescription = oStatement.GetColData(2); const char* pszSRSId = oStatement.GetColData(3); const char* pszMinX = oStatement.GetColData(4); const char* pszMinY = oStatement.GetColData(5); const char* pszMaxX = oStatement.GetColData(6); const char* pszMaxY = oStatement.GetColData(7); const char* pszTMSMinX = oStatement.GetColData(8); const char* pszTMSMinY = oStatement.GetColData(9); const char* pszTMSMaxX = oStatement.GetColData(10); const char* pszTMSMaxY = oStatement.GetColData(11); if( pszTableName != NULL && pszTMSMinX != NULL && pszTMSMinY != NULL && pszTMSMaxX != NULL && pszTMSMaxY != NULL ) { eAccess = GA_Update; //LATER - where should this be set? bRet = OpenRaster( pszTableName, pszIdentifier, pszDescription, pszSRSId ? atoi(pszSRSId) : 0, CPLAtof(pszTMSMinX), CPLAtof(pszTMSMinY), CPLAtof(pszTMSMaxX), CPLAtof(pszTMSMaxY), pszMinX, pszMinY, pszMaxX, pszMaxY, poOpenInfo->papszOpenOptions ); CPLDebug("OGRDB2DataSource::OpenNew","back from OpenRaster; bRet: %d", bRet); if (bRet) { return TRUE; } } } } CPLError(CE_Failure, CPLE_AppDefined, "Table '%s' not found", osSubdatasetTableName.c_str()); CPLDebug("OGRDB2DataSource::OpenNew","exiting FALSE"); return FALSE; } /************************************************************************/ /* InializeSession() */ /************************************************************************/ int OGRDB2DataSource::InitializeSession( const char * pszNewName, int bTestOpen ) { CPLDebug( "OGR_DB2DataSource::InitializeSession", "pszNewName: '%s'; bTestOpen: %d", pszNewName, bTestOpen); // If we already have a connection, assume that it is good and we don't // have to do anything else. if (m_oSession.GetConnection()) { CPLDebug("OGR_DB2DataSource::InitializeSession", "connected already: %p", m_oSession.GetConnection()); return TRUE; } /* Determine if the connection string contains specific values */ char* pszTableSpec = NULL; char* pszConnectionName = CPLStrdup(pszNewName + strlen(DB2ODBC_PREFIX)); char* pszDriver = NULL; int nCurrent, nNext, nTerm; nCurrent = nNext = nTerm = static_cast<int>(strlen(pszConnectionName)); while (nCurrent > 0) { --nCurrent; if (pszConnectionName[nCurrent] == ';') { nNext = nCurrent; continue; } if (ParseValue(&m_pszCatalog, pszConnectionName, "database=", nCurrent, nNext, nTerm, FALSE)) continue; if (ParseValue(&pszTableSpec, pszConnectionName, "tables=", nCurrent, nNext, nTerm, TRUE)) continue; if (ParseValue(&pszDriver, pszConnectionName, "driver=", nCurrent, nNext, nTerm, FALSE)) continue; } CPLDebug( "OGR_DB2DataSource::Open", "m_pszCatalog: '%s'", m_pszCatalog); CPLDebug( "OGR_DB2DataSource::Open", "pszTableSpec: '%s'", pszTableSpec); CPLDebug( "OGR_DB2DataSource::Open", "pszDriver: '%s'", pszDriver); CPLDebug( "OGR_DB2DataSource::Open", "pszConnectionName: '%s'", pszConnectionName); /* Determine if the connection string contains the database portion */ if( m_pszCatalog == NULL ) { CPLError( CE_Failure, CPLE_AppDefined, "'%s' does not contain the 'database' portion\n", pszNewName ); CPLFree(pszTableSpec); CPLFree(pszConnectionName); CPLFree(pszDriver); return FALSE; } m_pszName = CPLStrdup(pszNewName); // if the table parameter was specified, pull out the table names if( pszTableSpec != NULL ) { char **papszTableList; int i; papszTableList = CSLTokenizeString2( pszTableSpec, ",", 0 ); for( i = 0; i < CSLCount(papszTableList); i++ ) { char **papszQualifiedParts; // Get schema and table name papszQualifiedParts = CSLTokenizeString2( papszTableList[i], ".", 0 ); /* Find the geometry column name if specified */ if( CSLCount( papszQualifiedParts ) >= 1 ) { char* pszGeomColumnName = NULL; char* pos = strchr(papszQualifiedParts[ CSLCount( papszQualifiedParts ) - 1], '('); if (pos != NULL) { *pos = '\0'; pszGeomColumnName = pos+1; int len = static_cast<int>(strlen(pszGeomColumnName)); if (len > 0) pszGeomColumnName[len - 1] = '\0'; } m_papszGeomColumnNames = CSLAddString( m_papszGeomColumnNames, pszGeomColumnName ? pszGeomColumnName : ""); } if( CSLCount( papszQualifiedParts ) == 2 ) { m_papszSchemaNames = CSLAddString( m_papszSchemaNames, ToUpper(papszQualifiedParts[0])); m_papszTableNames = CSLAddString( m_papszTableNames, ToUpper(papszQualifiedParts[1])); } else if( CSLCount( papszQualifiedParts ) == 1 ) { m_papszSchemaNames = CSLAddString( m_papszSchemaNames, "NULL"); m_papszTableNames = CSLAddString( m_papszTableNames, ToUpper(papszQualifiedParts[0])); } CSLDestroy(papszQualifiedParts); } CSLDestroy(papszTableList); } CPLFree(pszTableSpec); /* Initialize the DB2 connection. */ int nResult; nResult = m_oSession.EstablishSession( pszConnectionName, "", "" ); CPLFree(pszDriver); if( !nResult ) { CPLError( CE_Failure, CPLE_AppDefined, "Unable to initialize connection to the server for %s,\n" "%s", pszNewName, m_oSession.GetLastError() ); CPLFree(pszConnectionName); return FALSE; } // Determine whether we are running on LUW or zoom OGRDB2Statement oStatement( &m_oSession ); oStatement.Append("SELECT COUNT(*) FROM SYSCAT.TABLES"); // We assume that if the statement fails, the table doesn't exist if( !oStatement.DB2Execute("OGR_DB2DataSource::InitializeSession") ) { CPLDebug("OGRDB2DataSource::InitializeSession","Must be z/OS"); m_bIsZ = TRUE; } else { CPLDebug("OGRDB2DataSource::InitializeSession","Must be LUW"); m_bIsZ = FALSE; } return TRUE; } /************************************************************************/ /* Open() */ /************************************************************************/ int OGRDB2DataSource::Open( const char * pszNewName, int bTestOpen ) { CPLAssert( m_nLayers == 0 ); CPLDebug( "OGR_DB2DataSource::Open", "pszNewName: '%s'; bTestOpen: %d", pszNewName, bTestOpen); if( !STARTS_WITH_CI(pszNewName, DB2ODBC_PREFIX) ) { if( !bTestOpen ) CPLError( CE_Failure, CPLE_AppDefined, "%s does not conform to DB2 naming convention," " DB2:*\n", pszNewName ); return FALSE; } if (!InitializeSession(pszNewName, bTestOpen)) { CPLDebug( "OGR_DB2DataSource::Open", "Session initialization failed"); return FALSE; } char** papszTypes = NULL; /* read metadata for the specified tables */ if (m_papszTableNames != NULL) { for( int iTable = 0; m_papszTableNames != NULL && m_papszTableNames[iTable] != NULL; iTable++ ) { int found = FALSE; OGRDB2Statement oStatement( &m_oSession ); // If a table name was specified, get the information from ST_Geometry_Columns // for this table oStatement.Appendf( "SELECT table_schema, column_name, 2, srs_id, " "srs_name, type_name " "FROM db2gse.st_geometry_columns " "WHERE table_name = '%s'", m_papszTableNames[iTable]); // If the schema was specified, add it to the SELECT statement if (strcmp(m_papszSchemaNames[iTable], "NULL")) oStatement.Appendf(" AND table_schema = '%s' ", m_papszSchemaNames[iTable]); if( oStatement.DB2Execute("OGR_DB2DataSource::Open") ) { while( oStatement.Fetch() ) { found = TRUE; /* set schema for table if it was not specified */ if (!strcmp(m_papszSchemaNames[iTable], "NULL")) { CPLFree(m_papszSchemaNames[iTable]); m_papszSchemaNames[iTable] = CPLStrdup( oStatement.GetColData(0) ); } if (m_papszGeomColumnNames == NULL) m_papszGeomColumnNames = CSLAddString( m_papszGeomColumnNames, oStatement.GetColData(1) ); else if (*m_papszGeomColumnNames[iTable] == 0) { CPLFree(m_papszGeomColumnNames[iTable]); m_papszGeomColumnNames[iTable] = CPLStrdup( oStatement.GetColData(1) ); } m_papszCoordDimensions = CSLAddString( m_papszCoordDimensions, oStatement.GetColData(2, "2") ); m_papszSRIds = CSLAddString( m_papszSRIds, oStatement.GetColData(3, "-1") ); m_papszSRTexts = CSLAddString( m_papszSRTexts, oStatement.GetColData(4, "") ); // Convert the DB2 spatial type to the OGC spatial type // which just entails stripping off the "ST_" at the // beginning of the DB2 type name char DB2SpatialType[20], OGCSpatialType [20]; strcpy(DB2SpatialType, oStatement.GetColData(5)); strcpy(OGCSpatialType, DB2SpatialType+3); // CPLDebug("OGR_DB2DataSource::Open","DB2SpatialType: %s, OGCSpatialType: %s",DB2SpatialType, OGCSpatialType); papszTypes = CSLAddString( papszTypes, OGCSpatialType ); } } if (!found) { CPLError( CE_Failure, CPLE_AppDefined, "Table %s.%s not found in " "db2gse.st_geometry_columns" , m_papszSchemaNames[iTable], m_papszTableNames[iTable] ); return FALSE; } } } /* Determine the available tables if not specified. */ if (m_papszTableNames == NULL) { OGRDB2Statement oStatement( &m_oSession ); oStatement.Append( "SELECT table_schema, table_name, column_name, 2, " "srs_id, srs_name, type_name " "FROM db2gse.st_geometry_columns"); if( oStatement.DB2Execute("OGR_DB2DataSource::Open") ) { while( oStatement.Fetch() ) { m_papszSchemaNames = CSLAddString( m_papszSchemaNames, oStatement.GetColData(0) ); m_papszTableNames = CSLAddString( m_papszTableNames, oStatement.GetColData(1) ); m_papszGeomColumnNames = CSLAddString( m_papszGeomColumnNames, oStatement.GetColData(2) ); m_papszCoordDimensions = CSLAddString( m_papszCoordDimensions, oStatement.GetColData(3) ); m_papszSRIds = CSLAddString( m_papszSRIds, oStatement.GetColData(4,"-1") ); m_papszSRTexts = CSLAddString( m_papszSRTexts, oStatement.GetColData(5,"") ); char DB2SpatialType[20], OGCSpatialType [20]; strcpy(DB2SpatialType, oStatement.GetColData(6)); strcpy(OGCSpatialType, DB2SpatialType+3); // CPLDebug("OGR_DB2DataSource::Open","DB2SpatialType: %s, OGCSpatialType: %s",DB2SpatialType, OGCSpatialType); papszTypes = CSLAddString( papszTypes, OGCSpatialType ); } } } /* CPLDebug("OGR_DB2DataSource::Open", "m_papszSchemaNames"); CSLPrint(m_papszSchemaNames, stderr); CPLDebug("OGR_DB2DataSource::Open", "m_papszTableNames"); CSLPrint(m_papszTableNames, stderr); CPLDebug("OGR_DB2DataSource::Open", "m_papszGeomColumnNames"); CSLPrint(m_papszGeomColumnNames, stderr); CPLDebug("OGR_DB2DataSource::Open", "m_papszSRIds"); CSLPrint(m_papszSRIds, stderr); CPLDebug("OGR_DB2DataSource::Open", "m_papszSRTexts"); CSLPrint(m_papszSRTexts, stderr); CPLDebug("OGR_DB2DataSource::Open", "papszTypes"); CSLPrint(papszTypes, stderr); */ int nSRId, nCoordDimension; char * pszSRText = NULL; OGRwkbGeometryType eType; for( int iTable = 0; m_papszTableNames != NULL && m_papszTableNames[iTable] != NULL; iTable++ ) { pszSRText = NULL; nSRId = -1; if (m_papszSRIds != NULL) { nSRId = atoi(m_papszSRIds[iTable]); CPLDebug("OGR_DB2DataSource::Open", "iTable: %d; schema: %s; " "table: %s; geomCol: %s; geomType: %s; srid: '%s'", iTable, m_papszSchemaNames[iTable], m_papszTableNames[iTable], m_papszGeomColumnNames[iTable], papszTypes[iTable], m_papszSRIds[iTable]); // If srid is not defined it was probably because the table // was not registered. // In that case, try to get it from the actual data table. if (nSRId < 0) { OGRDB2Statement oStatement( &m_oSession ); oStatement.Appendf( "select db2gse.st_srsid(%s) from %s.%s " "fetch first row only", m_papszGeomColumnNames[iTable], m_papszSchemaNames[iTable], m_papszTableNames[iTable] ); if( oStatement.DB2Execute("OGR_DB2DataSource::Open") && oStatement.Fetch()) { nSRId = atoi( oStatement.GetColData( 0 ) ); OGRDB2Statement oStatement2( &m_oSession ); oStatement2.Appendf( "select definition from " "db2gse.st_spatial_reference_systems " "where srs_id = %d", nSRId); if( oStatement2.DB2Execute("OGR_DB2DataSource::Open") && oStatement2.Fetch() ) { if ( oStatement2.GetColData( 0 ) ) pszSRText = CPLStrdup(oStatement2.GetColData( 0 )); } } if (nSRId < 0) { // something went wrong - didn't find srid - use default nSRId = 0; pszSRText = CPLStrdup("UNSPECIFIED"); CPLDebug("OGR_DB2DataSource::Open", "Using default srid 0"); } } else { pszSRText = CPLStrdup(m_papszSRTexts[iTable]); } } CPLDebug("OGR_DB2DataSource::Open", "nSRId: %d; srText: %s", nSRId, pszSRText); if (m_papszCoordDimensions != NULL) nCoordDimension = atoi(m_papszCoordDimensions[iTable]); else nCoordDimension = 2; if (papszTypes != NULL) eType = OGRFromOGCGeomType(papszTypes[iTable]); else eType = wkbUnknown; if( strlen(m_papszGeomColumnNames[iTable]) > 0 ) OpenTable( m_papszSchemaNames[iTable], m_papszTableNames[iTable], m_papszGeomColumnNames[iTable], nCoordDimension, nSRId, pszSRText, eType); else OpenTable( m_papszSchemaNames[iTable], m_papszTableNames[iTable], NULL, nCoordDimension, nSRId, pszSRText, wkbNone); } CPLFree(pszSRText); bDSUpdate = m_bUpdate; //LATER what is bDSUpdate? return TRUE; } /************************************************************************/ /* ExecuteSQL() */ /************************************************************************/ OGRLayer * OGRDB2DataSource::ExecuteSQL( const char *pszSQLCommand, OGRGeometry *poSpatialFilter, const char *pszDialect ) { /* -------------------------------------------------------------------- */ /* Use generic implementation for recognized dialects */ /* -------------------------------------------------------------------- */ CPLDebug("OGRDB2DataSource::ExecuteSQL", "SQL: '%s'; dialect: '%s'", pszSQLCommand, pszDialect); if( IsGenericSQLDialect(pszDialect) ) return GDALDataset::ExecuteSQL( pszSQLCommand, poSpatialFilter, pszDialect ); /* -------------------------------------------------------------------- */ /* Special case DELLAYER: command. */ /* -------------------------------------------------------------------- */ if( EQUALN(pszSQLCommand,"DELLAYER:",9) ) { const char *pszLayerName = pszSQLCommand + 9; while( *pszLayerName == ' ' ) pszLayerName++; OGRLayer* poLayer = GetLayerByName(pszLayerName); for( int iLayer = 0; iLayer < m_nLayers; iLayer++ ) { if( m_papoLayers[iLayer] == poLayer ) { DeleteLayer( iLayer ); break; } } return NULL; } CPLDebug( "OGRDB2DataSource::ExecuteSQL", "ExecuteSQL(%s) called.", pszSQLCommand ); /* Execute the command natively */ OGRDB2Statement *poStatement = new OGRDB2Statement( &m_oSession ); poStatement->Append( pszSQLCommand ); if( !poStatement->ExecuteSQL() ) { CPLError( CE_Failure, CPLE_AppDefined, "%s", m_oSession.GetLastError() ); delete poStatement; return NULL; } /* -------------------------------------------------------------------- */ /* Are there result columns for this statement? */ /* -------------------------------------------------------------------- */ if( poStatement->GetColCount() == 0 ) { delete poStatement; CPLErrorReset(); return NULL; } /* -------------------------------------------------------------------- */ /* Create a results layer. It will take ownership of the */ /* statement. */ /* -------------------------------------------------------------------- */ OGRDB2SelectLayer *poLayer = NULL; poLayer = new OGRDB2SelectLayer( this, poStatement ); if( poSpatialFilter != NULL ) poLayer->SetSpatialFilter( poSpatialFilter ); return poLayer; } /************************************************************************/ /* ReleaseResultSet() */ /************************************************************************/ void OGRDB2DataSource::ReleaseResultSet( OGRLayer * poLayer ) { delete poLayer; } /************************************************************************/ /* ToUpper() */ /************************************************************************/ char *OGRDB2DataSource::ToUpper( const char *pszSrcName ) { char *pszSafeName = CPLStrdup( pszSrcName ); int i; for( i = 0; pszSafeName[i] != '\0'; i++ ) { pszSafeName[i] = (char) toupper( pszSafeName[i] ); } return pszSafeName; } /************************************************************************/ /* LaunderName() */ /************************************************************************/ char *OGRDB2DataSource::LaunderName( const char *pszSrcName ) { char *pszSafeName = CPLStrdup( pszSrcName ); int i; for( i = 0; pszSafeName[i] != '\0'; i++ ) { pszSafeName[i] = (char) tolower( pszSafeName[i] ); if( pszSafeName[i] == '-' || pszSafeName[i] == '#' ) pszSafeName[i] = '_'; } return pszSafeName; } /************************************************************************/ /* InitializeMetadataTables() */ /* */ /* Create the metadata tables (SPATIAL_REF_SYS and */ /* GEOMETRY_COLUMNS). */ /************************************************************************/ OGRErr OGRDB2DataSource::InitializeMetadataTables() { CPLDebug( "OGR_DB2DataSource::InitializeMetadataTables", "Not supported"); CPLError( CE_Failure, CPLE_AppDefined, "Dynamically creating DB2 spatial metadata tables is " "not supported" ); return OGRERR_FAILURE; } /************************************************************************/ /* FetchSRS() */ /* */ /* Return a SRS corresponding to a particular id. Note that */ /* reference counting should be honoured on the returned */ /* OGRSpatialReference, as handles may be cached. */ /************************************************************************/ OGRSpatialReference *OGRDB2DataSource::FetchSRS( int nId ) { CPLDebug("OGRDB2DataSource::FetchSRS", "nId: %d", nId); if( nId <= 0 ) return NULL; /* -------------------------------------------------------------------- */ /* First, we look through our SRID cache, is it there? */ /* -------------------------------------------------------------------- */ int i; for( i = 0; i < m_nKnownSRID; i++ ) { if( m_panSRID[i] == nId ) return m_papoSRS[i]; } OGRSpatialReference *poSRS = NULL; /* -------------------------------------------------------------------- */ /* Try looking up in spatial_ref_sys table */ /* -------------------------------------------------------------------- */ if (bUseGeometryColumns) { OGRDB2Statement oStatement( GetSession() ); oStatement.Appendf( "SELECT definition FROM " "db2gse.st_spatial_reference_systems " "WHERE srs_id = %d", nId ); if( oStatement.ExecuteSQL() && oStatement.Fetch() ) { if ( oStatement.GetColData( 0 ) ) { poSRS = new OGRSpatialReference(); char* pszWKT = (char*)oStatement.GetColData( 0 ); CPLDebug("OGR_DB2DataSource::FetchSRS", "SRS = %s", pszWKT); if( poSRS->importFromWkt( &pszWKT ) != OGRERR_NONE ) { delete poSRS; poSRS = NULL; } } } } /* -------------------------------------------------------------------- */ /* Try looking up the EPSG list */ /* -------------------------------------------------------------------- */ if (!poSRS) { poSRS = new OGRSpatialReference(); if( poSRS->importFromEPSG( nId ) != OGRERR_NONE ) { delete poSRS; poSRS = NULL; } } /* -------------------------------------------------------------------- */ /* Add to the cache. */ /* -------------------------------------------------------------------- */ if (poSRS) { m_panSRID = (int *) CPLRealloc(m_panSRID,sizeof(int) * (m_nKnownSRID+1) ); m_papoSRS = (OGRSpatialReference **) CPLRealloc(m_papoSRS, sizeof(void*) * (m_nKnownSRID + 1) ); m_panSRID[m_nKnownSRID] = nId; m_papoSRS[m_nKnownSRID] = poSRS; m_nKnownSRID++; CPLDebug("OGRDB2DataSource::FetchSRS", "Add to cache; m_nKnownSRID: %d", m_nKnownSRID); } return poSRS; } /************************************************************************/ /* FetchSRSId() */ /* */ /* Fetch the id corresponding to an SRS, and if not found, add */ /* it to the table. */ /************************************************************************/ int OGRDB2DataSource::FetchSRSId( OGRSpatialReference * poSRS) { char *pszWKT = NULL; int nSRSId = 0; const char* pszAuthorityName; if( poSRS == NULL ) return 0; OGRSpatialReference oSRS(*poSRS); // cppcheck-suppress uselessAssignmentPtrArg poSRS = NULL; pszAuthorityName = oSRS.GetAuthorityName(NULL); CPLDebug("OGRDB2DataSource::FetchSRSId", "pszAuthorityName: '%s'", pszAuthorityName); if( pszAuthorityName == NULL || strlen(pszAuthorityName) == 0 ) { /* -----------------------------------------------------------------*/ /* Try to identify an EPSG code */ /* -----------------------------------------------------------------*/ oSRS.AutoIdentifyEPSG(); pszAuthorityName = oSRS.GetAuthorityName(NULL); if (pszAuthorityName != NULL && EQUAL(pszAuthorityName, "EPSG")) { const char* pszAuthorityCode = oSRS.GetAuthorityCode(NULL); if ( pszAuthorityCode != NULL && strlen(pszAuthorityCode) > 0 ) { /* Import 'clean' SRS */ oSRS.importFromEPSG( atoi(pszAuthorityCode) ); pszAuthorityName = oSRS.GetAuthorityName(NULL); } } } /* -------------------------------------------------------------------- */ /* Check whether the EPSG authority code is already mapped to a */ /* SRS ID. */ /* -------------------------------------------------------------------- */ int nAuthorityCode = 0; if( pszAuthorityName != NULL && EQUAL( pszAuthorityName, "EPSG" ) ) { /* For the root authority name 'EPSG', the authority code * should always be integral */ nAuthorityCode = atoi( oSRS.GetAuthorityCode(NULL) ); OGRDB2Statement oStatement( &m_oSession ); oStatement.Appendf("SELECT srs_id " "FROM db2gse.st_spatial_reference_systems WHERE " "organization = '%s' " "AND organization_coordsys_id = %d", pszAuthorityName, nAuthorityCode ); if( oStatement.DB2Execute("OGR_DB2DataSource::FetchSRSId") && oStatement.Fetch() && oStatement.GetColData( 0 ) ) { nSRSId = atoi(oStatement.GetColData( 0 )); CPLDebug("OGR_DB2DataSource::FetchSRSId", "nSRSId = %d", nSRSId); return nSRSId; } } /* -------------------------------------------------------------------- */ /* Translate SRS to WKT. */ /* -------------------------------------------------------------------- */ if( oSRS.exportToWkt( &pszWKT ) != OGRERR_NONE ) { CPLFree(pszWKT); return 0; } /* -------------------------------------------------------------------- */ /* Try to find in the existing table. */ /* -------------------------------------------------------------------- */ OGRDB2Statement oStatement( &m_oSession ); oStatement.Append( "SELECT srs_id FROM db2gse.st_spatial_reference_systems " "WHERE description = "); OGRDB2AppendEscaped(&oStatement, pszWKT); /* -------------------------------------------------------------------- */ /* We got it! Return it. */ /* -------------------------------------------------------------------- */ if( oStatement.DB2Execute("OGR_DB2DataSource::FetchSRSId") ) { if ( oStatement.Fetch() && oStatement.GetColData( 0 ) ) { nSRSId = atoi(oStatement.GetColData( 0 )); CPLFree(pszWKT); return nSRSId; } } else { CPLError( CE_Failure, CPLE_AppDefined, "Didn't find srs_id for %s", pszWKT ); } return -1; } /************************************************************************/ /* StartTransaction() */ /* */ /* Should only be called by user code. Not driver internals. */ /************************************************************************/ OGRErr OGRDB2DataSource::StartTransaction(CPL_UNUSED int bForce) { if (!m_oSession.BeginTransaction()) { CPLError( CE_Failure, CPLE_AppDefined, "Failed to start transaction: %s", m_oSession.GetLastError()); return OGRERR_FAILURE; } return OGRERR_NONE; } /************************************************************************/ /* CommitTransaction() */ /* */ /* Should only be called by user code. Not driver internals. */ /************************************************************************/ OGRErr OGRDB2DataSource::CommitTransaction() { if (!m_oSession.CommitTransaction()) { CPLError( CE_Failure, CPLE_AppDefined, "Failed to commit transaction: %s", m_oSession.GetLastError() ); for( int iLayer = 0; iLayer < m_nLayers; iLayer++ ) { if( m_papoLayers[iLayer]->GetLayerStatus() == DB2LAYERSTATUS_INITIAL ) m_papoLayers[iLayer]->SetLayerStatus(DB2LAYERSTATUS_DISABLED); } return OGRERR_FAILURE; } /* set the status for the newly created layers */ for( int iLayer = 0; iLayer < m_nLayers; iLayer++ ) { if( m_papoLayers[iLayer]->GetLayerStatus() == DB2LAYERSTATUS_INITIAL ) m_papoLayers[iLayer]->SetLayerStatus(DB2LAYERSTATUS_CREATED); } return OGRERR_NONE; } /************************************************************************/ /* RollbackTransaction() */ /* */ /* Should only be called by user code. Not driver internals. */ /************************************************************************/ OGRErr OGRDB2DataSource::RollbackTransaction() { /* set the status for the newly created layers */ for( int iLayer = 0; iLayer < m_nLayers; iLayer++ ) { if( m_papoLayers[iLayer]->GetLayerStatus() == DB2LAYERSTATUS_INITIAL ) m_papoLayers[iLayer]->SetLayerStatus(DB2LAYERSTATUS_DISABLED); } if (!m_oSession.RollbackTransaction()) { CPLError( CE_Failure, CPLE_AppDefined, "Failed to roll back transaction: %s", m_oSession.GetLastError() ); return OGRERR_FAILURE; } return OGRERR_NONE; } /************************************************************************/ /* OpenRaster() */ /************************************************************************/ int OGRDB2DataSource::OpenRaster( const char* pszTableName, const char* pszIdentifier, const char* pszDescription, int nSRSId, double dfMinX, double dfMinY, double dfMaxX, double dfMaxY, const char* pszContentsMinX, const char* pszContentsMinY, const char* pszContentsMaxX, const char* pszContentsMaxY, char** papszOpenOptionsIn ) { if( dfMinX >= dfMaxX || dfMinY >= dfMaxY ) return FALSE; CPLDebug("OGRDB2DataSource::OpenRaster", "pszTableName: '%s'", pszTableName); m_bRecordInsertedInGPKGContent = TRUE; m_nSRID = nSRSId; if( nSRSId > 0 ) { OGRSpatialReference* poSRS = FetchSRS( nSRSId ); CPLDebug("OGRDB2DataSource::OpenRaster", "nSRSId: '%d'", nSRSId); if( poSRS ) { poSRS->exportToWkt(&m_pszProjection); CPLDebug("OGRDB2DataSource::OpenRaster", "m_pszProjection: '%s'", m_pszProjection); //LATER delete poSRS; } } OGRDB2Statement oStatement( &m_oSession ); oStatement.Appendf( "SELECT zoom_level, pixel_x_size, pixel_y_size, " "tile_width, tile_height, matrix_width, matrix_height " "FROM gpkg.tile_matrix tm " "WHERE table_name = '%s' AND pixel_x_size > 0 " "AND pixel_y_size > 0 AND tile_width > 0 " "AND tile_height > 0 AND matrix_width > 0 " "AND matrix_height > 0", pszTableName); char* pszSQL = CPLStrdup(oStatement.GetCommand()); // save to use later int bGotRow = FALSE; const char* pszZoomLevel = CSLFetchNameValue(papszOpenOptionsIn, "ZOOM_LEVEL"); CPLDebug("OGRDB2DataSource::OpenRaster", "pszZoomLevel: '%s', m_bUpdate: %d", pszZoomLevel, m_bUpdate); if( pszZoomLevel ) { if( m_bUpdate ) oStatement.Appendf(" AND zoom_level <= %d", atoi(pszZoomLevel)); else { oStatement.Appendf(" AND (zoom_level = %d OR (zoom_level < %d " "AND EXISTS(SELECT 1 FROM '%s' " "WHERE zoom_level = tm.zoom_level FETCH FIRST ROW ONLY)))", atoi(pszZoomLevel), atoi(pszZoomLevel), pszTableName); } } // In read-only mode, only lists non empty zoom levels else if( !m_bUpdate ) { oStatement.Appendf(" AND EXISTS(SELECT 1 FROM %s " "WHERE zoom_level = tm.zoom_level FETCH FIRST ROW ONLY)", pszTableName); } else if( pszZoomLevel == NULL ) { oStatement.Appendf(" AND zoom_level <= (SELECT MAX(zoom_level) FROM %s)", pszTableName); } oStatement.Appendf(" ORDER BY zoom_level DESC"); if( oStatement.DB2Execute("OGR_DB2DataSource::OpenRaster")) { if( oStatement.Fetch()) { CPLDebug("OGR_DB2DataSource::OpenRaster", "col 0: %s", oStatement.GetColData(0)); bGotRow = TRUE; } else { CPLDebug("OGR_DB2DataSource::OpenRaster", "Fetch1 failed"); } } else { CPLError( CE_Failure, CPLE_AppDefined, "%s", m_oSession.GetLastError() ); CPLFree(pszSQL); return FALSE; } if( !bGotRow && pszContentsMinX != NULL && pszContentsMinY != NULL && pszContentsMaxX != NULL && pszContentsMaxY != NULL ) { oStatement.Clear(); oStatement.Append(pszSQL); oStatement.Append(" ORDER BY zoom_level DESC FETCH FIRST ROW ONLY"); CPLDebug("OGR_DB2DataSource::OpenRaster", "SQL: %s", oStatement.GetCommand()); if( oStatement.DB2Execute("OGR_DB2DataSource::OpenRaster")) { if( oStatement.Fetch()) { CPLDebug("OGR_DB2DataSource::OpenRaster", "col 0: %s", oStatement.GetColData(0)); bGotRow = TRUE; } } else { CPLError( CE_Failure, CPLE_AppDefined, "%s", m_oSession.GetLastError() ); CPLDebug("OGR_DB2DataSource::OpenRaster", "error: %s", m_oSession.GetLastError() ); CPLFree(pszSQL); return FALSE; } } if( !bGotRow ) { CPLFree(pszSQL); return FALSE; } CPLFree(pszSQL); OGRDB2Statement oStatement2( &m_oSession ); // If USE_TILE_EXTENT=YES, then query the tile table to find which tiles // actually exist. CPLString osContentsMinX, osContentsMinY, osContentsMaxX, osContentsMaxY; if( CPLTestBool(CSLFetchNameValueDef(papszOpenOptionsIn, "USE_TILE_EXTENT", "NO")) ) { oStatement2.Appendf( "SELECT MIN(tile_column), MIN(tile_row), MAX(tile_column), MAX(tile_row) FROM %s WHERE zoom_level = %d", pszTableName, atoi(oStatement.GetColData(0))); if( oStatement2.DB2Execute("OGR_DB2DataSource::OpenRaster")) { if( oStatement2.Fetch()) { CPLDebug("OGR_DB2DataSource::OpenRaster", "col 0: %s", oStatement2.GetColData(0)); } else { return FALSE; } } else { CPLError( CE_Failure, CPLE_AppDefined, "%s", m_oSession.GetLastError() ); CPLDebug("OGR_DB2DataSource::OpenRaster", "error: %s", m_oSession.GetLastError() ); return FALSE; } double dfPixelXSize = CPLAtof(oStatement.GetColData( 1)); double dfPixelYSize = CPLAtof(oStatement.GetColData( 2)); int nTileWidth = atoi(oStatement.GetColData( 3)); int nTileHeight = atoi(oStatement.GetColData( 4)); osContentsMinX = CPLSPrintf("%.18g", dfMinX + dfPixelXSize * nTileWidth * atoi(oStatement2.GetColData( 0))); osContentsMaxY = CPLSPrintf("%.18g", dfMaxY - dfPixelYSize * nTileHeight * atoi(oStatement2.GetColData( 1))); osContentsMaxX = CPLSPrintf("%.18g", dfMinX + dfPixelXSize * nTileWidth * (1 + atoi(oStatement2.GetColData( 2)))); osContentsMinY = CPLSPrintf("%.18g", dfMaxY - dfPixelYSize * nTileHeight * (1 + atoi(oStatement2.GetColData( 3)))); pszContentsMinX = osContentsMinX.c_str(); pszContentsMinY = osContentsMinY.c_str(); pszContentsMaxX = osContentsMaxX.c_str(); pszContentsMaxY = osContentsMaxY.c_str(); } if(! InitRaster ( NULL, pszTableName, dfMinX, dfMinY, dfMaxX, dfMaxY, pszContentsMinX, pszContentsMinY, pszContentsMaxX, pszContentsMaxY, papszOpenOptionsIn, &oStatement, 0) ) { return FALSE; } CheckUnknownExtensions(TRUE); // Do this after CheckUnknownExtensions() so that m_eTF is set to GPKG_TF_WEBP // if the table already registers the gpkg_webp extension const char* pszTF = CSLFetchNameValue(papszOpenOptionsIn, "TILE_FORMAT"); if( pszTF ) { if( !m_bUpdate ) { CPLError(CE_Warning, CPLE_AppDefined, "DRIVER open option ignored in read-only mode"); } else { GPKGTileFormat eTF = GetTileFormat(pszTF); if( eTF == GPKG_TF_WEBP && m_eTF != eTF ) { if( !RegisterWebPExtension() ) return FALSE; } m_eTF = eTF; } } ParseCompressionOptions(papszOpenOptionsIn); CPLDebug("OGR_DB2DataSource::OpenRaster", "after ParseCompress"); m_osWHERE = CSLFetchNameValueDef(papszOpenOptionsIn, "WHERE", ""); // Set metadata if( pszIdentifier && pszIdentifier[0] ) GDALPamDataset::SetMetadataItem("IDENTIFIER", pszIdentifier); if( pszDescription && pszDescription[0] ) GDALPamDataset::SetMetadataItem("DESCRIPTION", pszDescription); CPLDebug("OGR_DB2DataSource::OpenRaster", "check for overviews"); // Add overviews for( int i = 1; oStatement.Fetch(); i++ ) { CPLDebug("OGR_DB2DataSource::OpenRaster", "fetch overview row: %d", i); OGRDB2DataSource* poOvrDS = new OGRDB2DataSource(); poOvrDS->InitRaster ( this, pszTableName, dfMinX, dfMinY, dfMaxX, dfMaxY, pszContentsMinX, pszContentsMinY, pszContentsMaxX, pszContentsMaxY, papszOpenOptionsIn, &oStatement, i); m_papoOverviewDS = (OGRDB2DataSource**) CPLRealloc(m_papoOverviewDS, sizeof(OGRDB2DataSource*) * (m_nOverviewCount+1)); m_papoOverviewDS[m_nOverviewCount ++] = poOvrDS; int nTileWidth, nTileHeight; poOvrDS->GetRasterBand(1)->GetBlockSize(&nTileWidth, &nTileHeight); if( poOvrDS->GetRasterXSize() < nTileWidth && poOvrDS->GetRasterYSize() < nTileHeight ) { break; } } CPLDebug("OGR_DB2DataSource::OpenRaster", "exiting"); return TRUE; } /************************************************************************/ /* InitRaster() */ /************************************************************************/ int OGRDB2DataSource::InitRaster ( OGRDB2DataSource* poParentDS, const char* pszTableName, double dfMinX, double dfMinY, double dfMaxX, double dfMaxY, const char* pszContentsMinX, const char* pszContentsMinY, const char* pszContentsMaxX, const char* pszContentsMaxY, char** papszOpenOptionsIn, OGRDB2Statement* oStatement, int nIdxInResult ) { m_osRasterTable = pszTableName; m_dfTMSMinX = dfMinX; m_dfTMSMaxY = dfMaxY; CPLDebug("OGRDB2DataSource::InitRaster1", "nIdxInResult: %d", nIdxInResult); if (nIdxInResult > 0) { CPLDebug("OGRDB2DataSource::InitRaster1", "Serious problem as we don't support nIdxInResult"); } int nZoomLevel = atoi(oStatement->GetColData( 0)); double dfPixelXSize = CPLAtof(oStatement->GetColData( 1)); double dfPixelYSize = CPLAtof(oStatement->GetColData( 2)); int nTileWidth = atoi(oStatement->GetColData( 3)); int nTileHeight = atoi(oStatement->GetColData( 4)); int nTileMatrixWidth = atoi(oStatement->GetColData( 5)); int nTileMatrixHeight = atoi(oStatement->GetColData( 6)); /* Use content bounds in priority over tile_matrix_set bounds */ double dfGDALMinX = dfMinX; double dfGDALMinY = dfMinY; double dfGDALMaxX = dfMaxX; double dfGDALMaxY = dfMaxY; pszContentsMinX = CSLFetchNameValueDef(papszOpenOptionsIn, "MINX", pszContentsMinX); pszContentsMinY = CSLFetchNameValueDef(papszOpenOptionsIn, "MINY", pszContentsMinY); pszContentsMaxX = CSLFetchNameValueDef(papszOpenOptionsIn, "MAXX", pszContentsMaxX); pszContentsMaxY = CSLFetchNameValueDef(papszOpenOptionsIn, "MAXY", pszContentsMaxY); if( pszContentsMinX != NULL && pszContentsMinY != NULL && pszContentsMaxX != NULL && pszContentsMaxY != NULL ) { dfGDALMinX = CPLAtof(pszContentsMinX); dfGDALMinY = CPLAtof(pszContentsMinY); dfGDALMaxX = CPLAtof(pszContentsMaxX); dfGDALMaxY = CPLAtof(pszContentsMaxY); } if( dfGDALMinX >= dfGDALMaxX || dfGDALMinY >= dfGDALMaxY ) { return FALSE; } int nBandCount = atoi(CSLFetchNameValueDef(papszOpenOptionsIn, "BAND_COUNT", "4")); if( nBandCount != 1 && nBandCount != 2 && nBandCount != 3 && nBandCount != 4 ) nBandCount = 4; return InitRaster(poParentDS, pszTableName, nZoomLevel, nBandCount, dfMinX, dfMaxY, dfPixelXSize, dfPixelYSize, nTileWidth, nTileHeight, nTileMatrixWidth, nTileMatrixHeight, dfGDALMinX, dfGDALMinY, dfGDALMaxX, dfGDALMaxY ); } /************************************************************************/ /* InitRaster() */ /************************************************************************/ int OGRDB2DataSource::InitRaster ( OGRDB2DataSource* poParentDS, const char* pszTableName, int nZoomLevel, int nBandCount, double dfTMSMinX, double dfTMSMaxY, double dfPixelXSize, double dfPixelYSize, int nTileWidth, int nTileHeight, int nTileMatrixWidth, int nTileMatrixHeight, double dfGDALMinX, double dfGDALMinY, double dfGDALMaxX, double dfGDALMaxY ) { CPLDebug("OGRDB2DataSource::InitRaster2","Entering"); m_osRasterTable = pszTableName; m_dfTMSMinX = dfTMSMinX; m_dfTMSMaxY = dfTMSMaxY; m_nZoomLevel = nZoomLevel; m_nTileMatrixWidth = nTileMatrixWidth; m_nTileMatrixHeight = nTileMatrixHeight; m_bGeoTransformValid = TRUE; m_adfGeoTransform[0] = dfGDALMinX; m_adfGeoTransform[1] = dfPixelXSize; m_adfGeoTransform[3] = dfGDALMaxY; m_adfGeoTransform[5] = -dfPixelYSize; double dfRasterXSize = 0.5 + (dfGDALMaxX - dfGDALMinX) / dfPixelXSize; double dfRasterYSize = 0.5 + (dfGDALMaxY - dfGDALMinY) / dfPixelYSize; if( dfRasterXSize > INT_MAX || dfRasterYSize > INT_MAX ) return FALSE; nRasterXSize = (int)dfRasterXSize; nRasterYSize = (int)dfRasterYSize; m_pabyCachedTiles = (GByte*) VSI_MALLOC3_VERBOSE(4 * 4, nTileWidth, nTileHeight); if( m_pabyCachedTiles == NULL ) { return FALSE; } for(int i = 1; i <= nBandCount; i ++) SetBand( i, new GDALDB2RasterBand(this, i, nTileWidth, nTileHeight) ); ComputeTileAndPixelShifts(); GDALPamDataset::SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE"); GDALPamDataset::SetMetadataItem("ZOOM_LEVEL", CPLSPrintf("%d", m_nZoomLevel)); if( poParentDS ) { m_poParentDS = poParentDS; m_bUpdate = poParentDS->m_bUpdate; eAccess = poParentDS->eAccess; m_eTF = poParentDS->m_eTF; m_nQuality = poParentDS->m_nQuality; m_nZLevel = poParentDS->m_nZLevel; m_bDither = poParentDS->m_bDither; /*m_nSRID = poParentDS->m_nSRID;*/ m_osWHERE = poParentDS->m_osWHERE; SetDescription(CPLSPrintf("%s - zoom_level=%d", poParentDS->GetDescription(), m_nZoomLevel)); } return TRUE; } /************************************************************************/ /* GetTileFormat() */ /************************************************************************/ static GPKGTileFormat GetTileFormat(const char* pszTF ) { GPKGTileFormat eTF = GPKG_TF_PNG_JPEG; if( pszTF ) { if( EQUAL(pszTF, "PNG_JPEG") ) eTF = GPKG_TF_PNG_JPEG; else if( EQUAL(pszTF, "PNG") ) eTF = GPKG_TF_PNG; else if( EQUAL(pszTF, "PNG8") ) eTF = GPKG_TF_PNG8; else if( EQUAL(pszTF, "JPEG") ) eTF = GPKG_TF_JPEG; else if( EQUAL(pszTF, "WEBP") ) eTF = GPKG_TF_WEBP; } return eTF; } /************************************************************************/ /* RegisterWebPExtension() */ /************************************************************************/ int OGRDB2DataSource::RegisterWebPExtension() { CPLDebug("OGRDB2DataSource::RegisterWebPExtension", "NO-OP"); CreateExtensionsTableIfNecessary(); #ifdef LATER char* pszSQL = sqlite3_mprintf( "INSERT INTO gpkg_extensions " "(table_name, column_name, extension_name, definition, scope) " "VALUES " "('%q', 'tile_data', 'gpkg_webp', 'GeoPackage 1.0 Specification Annex P', 'read-write')", m_osRasterTable.c_str()); OGRErr eErr = SQLCommand(hDB, pszSQL); sqlite3_free(pszSQL); if ( OGRERR_NONE != eErr ) return FALSE; #endif return TRUE; } /************************************************************************/ /* CheckUnknownExtensions() */ /************************************************************************/ void OGRDB2DataSource::CheckUnknownExtensions(int /*bCheckRasterTable*/) { if( !HasExtensionsTable() ) return; CPLDebug("OGRDB2DataSource::CheckUnknownExtensions","NO-OP"); #ifdef LATER char* pszSQL; if( !bCheckRasterTable) pszSQL = sqlite3_mprintf( "SELECT extension_name, definition, scope FROM gpkg_extensions WHERE table_name IS NULL AND extension_name != 'gdal_aspatial'"); else pszSQL = sqlite3_mprintf( "SELECT extension_name, definition, scope FROM gpkg_extensions WHERE table_name = '%q'", m_osRasterTable.c_str()); SQLResult oResultTable; OGRErr err = SQLQuery(GetDB(), pszSQL, &oResultTable); sqlite3_free(pszSQL); if ( err == OGRERR_NONE && oResultTable.nRowCount > 0 ) { for(int i=0; i<oResultTable.nRowCount; i++) { const char* pszExtName = SQLResultGetValue(&oResultTable, 0, i); const char* pszDefinition = SQLResultGetValue(&oResultTable, 1, i); const char* pszScope = SQLResultGetValue(&oResultTable, 2, i); if( pszExtName == NULL ) pszExtName = "(null)"; if( pszDefinition == NULL ) pszDefinition = "(null)"; if( pszScope == NULL ) pszScope = "(null)"; if( EQUAL(pszExtName, "gpkg_webp") ) { if( GDALGetDriverByName("WEBP") == NULL ) { CPLError(CE_Warning, CPLE_AppDefined, "Table %s contains WEBP tiles, but GDAL configured " "without WEBP support. Data will be missing", m_osRasterTable.c_str()); } m_eTF = GPKG_TF_WEBP; continue; } if( EQUAL(pszExtName, "gpkg_zoom_other") ) { m_bZoomOther = TRUE; continue; } if( GetUpdate() && EQUAL(pszScope, "write-only") ) { CPLError(CE_Warning, CPLE_AppDefined, "Database relies on the '%s' (%s) extension that should " "be implemented for safe write-support, but is not currently. " "Update of that database are strongly discouraged to avoid corruption.", pszExtName, pszDefinition); } else if( GetUpdate() && EQUAL(pszScope, "read-write") ) { CPLError(CE_Warning, CPLE_AppDefined, "Database relies on the '%s' (%s) extension that should " "be implemented in order to read/write it safely, but is not currently. " "Some data may be missing while reading that database, and updates are strongly discouraged.", pszExtName, pszDefinition); } else if( EQUAL(pszScope, "read-write") ) { CPLError(CE_Warning, CPLE_AppDefined, "Database relies on the '%s' (%s) extension that should " "be implemented in order to read it safely, but is not currently. " "Some data may be missing while reading that database.", pszExtName, pszDefinition); } } } SQLResultFree(&oResultTable); #endif } /************************************************************************/ /* ParseCompressionOptions() */ /************************************************************************/ void OGRDB2DataSource::ParseCompressionOptions(char** papszOptions) { const char* pszZLevel = CSLFetchNameValue(papszOptions, "ZLEVEL"); if( pszZLevel ) m_nZLevel = atoi(pszZLevel); const char* pszQuality = CSLFetchNameValue(papszOptions, "QUALITY"); if( pszQuality ) m_nQuality = atoi(pszQuality); const char* pszDither = CSLFetchNameValue(papszOptions, "DITHER"); if( pszDither ) m_bDither = CPLTestBool(pszDither); } /************************************************************************/ /* ComputeTileAndPixelShifts() */ /************************************************************************/ void OGRDB2DataSource::ComputeTileAndPixelShifts() { CPLDebug("OGRDB2DataSource::ComputeTileAndPixelShifts", "Entering"); int nTileWidth, nTileHeight; GetRasterBand(1)->GetBlockSize(&nTileWidth, &nTileHeight); // Compute shift between GDAL origin and TileMatrixSet origin int nShiftXPixels = (int)floor(0.5 + (m_adfGeoTransform[0] - m_dfTMSMinX) / m_adfGeoTransform[1]); m_nShiftXTiles = (int)floor(1.0 * nShiftXPixels / nTileWidth); m_nShiftXPixelsMod = ((nShiftXPixels % nTileWidth) + nTileWidth) % nTileWidth; int nShiftYPixels = (int)floor(0.5 + (m_adfGeoTransform[3] - m_dfTMSMaxY) / m_adfGeoTransform[5]); m_nShiftYTiles = (int)floor(1.0 * nShiftYPixels / nTileHeight); m_nShiftYPixelsMod = ((nShiftYPixels % nTileHeight) + nTileHeight) % nTileHeight; } /************************************************************************/ /* CreateExtensionsTableIfNecessary() */ /************************************************************************/ OGRErr OGRDB2DataSource::CreateExtensionsTableIfNecessary() { /* Check if the table gpkg_extensions exists */ if( HasExtensionsTable() ) return OGRERR_NONE; CPLDebug("OGRDB2DataSource::CreateExtensionsTableIfNecessary", "NO-OP"); #ifdef LATER /* Requirement 79 : Every extension of a GeoPackage SHALL be registered */ /* in a corresponding row in the gpkg_extensions table. The absence of a */ /* gpkg_extensions table or the absence of rows in gpkg_extensions table */ /* SHALL both indicate the absence of extensions to a GeoPackage. */ const char* pszCreateGpkgExtensions = "CREATE TABLE gpkg_extensions (" "table_name TEXT," "column_name TEXT," "extension_name TEXT NOT NULL," "definition TEXT NOT NULL," "scope TEXT NOT NULL," "CONSTRAINT ge_tce UNIQUE (table_name, column_name, extension_name)" ")"; return SQLCommand(hDB, pszCreateGpkgExtensions); #endif return OGRERR_NONE; } /************************************************************************/ /* HasExtensionsTable() */ /************************************************************************/ int OGRDB2DataSource::HasExtensionsTable() { CPLDebug("OGRDB2DataSource::HasExtensionsTable", "NO-OP"); #ifdef LATER SQLResult oResultTable; OGRErr err = SQLQuery(hDB, "SELECT * FROM sqlite_master WHERE name = 'gpkg_extensions' " "AND type IN ('table', 'view')", &oResultTable); int bHasExtensionsTable = ( err == OGRERR_NONE && oResultTable.nRowCount == 1 ); SQLResultFree(&oResultTable); return bHasExtensionsTable; #endif return OGRERR_NONE; } /************************************************************************/ /* FlushCache() */ /************************************************************************/ void OGRDB2DataSource::FlushCache() { DB2_DEBUG_ENTER("OGRDB2DataSource::FlushCache"); FlushCacheWithErrCode(); DB2_DEBUG_EXIT("OGRDB2DataSource::FlushCache"); } CPLErr OGRDB2DataSource::FlushCacheWithErrCode() { CPLDebug("OGRDB2DataSource::FlushCacheWithErrCode","m_bInFlushCache %d", m_bInFlushCache); if( m_bInFlushCache ) return CE_None; m_bInFlushCache = TRUE; // Short circuit GDALPamDataset to avoid serialization to .aux.xml CPLDebug("OGRDB2DataSource::FlushCacheWithErrCode","calling GDALDataset::FlushCache"); GDALDataset::FlushCache(); /* Not sure what this has to do with raster operations for( int i = 0; i < m_nLayers; i++ ) { m_papoLayers[i]->RunDeferredCreationIfNecessary(); m_papoLayers[i]->CreateSpatialIndexIfNecessary(); } */ CPLErr eErr = CE_None; CPLDebug("OGRDB2DataSource::FlushCacheWithErrCode","m_bUpdate %d", m_bUpdate); if( m_bUpdate ) { if( m_nShiftXPixelsMod || m_nShiftYPixelsMod ) { eErr = FlushRemainingShiftedTiles(); } else { eErr = WriteTile(); } } OGRDB2DataSource* poMainDS = m_poParentDS ? m_poParentDS : this; CPLDebug("OGRDB2DataSource::FlushCacheWithErrCode", "m_nTileInsertionCount: %d", poMainDS->m_nTileInsertionCount); if( poMainDS->m_nTileInsertionCount ) { poMainDS->SoftCommitTransaction(); poMainDS->m_nTileInsertionCount = 0; } m_bInFlushCache = FALSE; CPLDebug("OGRDB2DataSource::FlushCacheWithErrCode","exiting; eErr: %d", eErr); return eErr; } /************************************************************************/ /* SoftStartTransaction() */ /************************************************************************/ int OGRDB2DataSource::SoftStartTransaction() { CPLDebug("OGRDB2DataSource::SoftStartTransaction", "enter"); return m_oSession.BeginTransaction(); } /************************************************************************/ /* SoftCommitTransaction() */ /************************************************************************/ int OGRDB2DataSource::SoftCommitTransaction() { CPLDebug("OGRDB2DataSource::SoftCommitTransaction", "enter"); return m_oSession.CommitTransaction(); } /************************************************************************/ /* SoftRollbackTransaction() */ /************************************************************************/ int OGRDB2DataSource::SoftRollbackTransaction() { CPLDebug("OGRDB2DataSource::SoftRollbackTransaction", "enter"); return m_oSession.RollbackTransaction(); } /************************************************************************/ /* CreateCopy() */ /************************************************************************/ typedef struct { const char* pszName; GDALResampleAlg eResampleAlg; } WarpResamplingAlg; static const WarpResamplingAlg asResamplingAlg[] = { { "BILINEAR", GRA_Bilinear }, { "CUBIC", GRA_Cubic }, { "CUBICSPLINE", GRA_CubicSpline }, { "LANCZOS", GRA_Lanczos }, { "MODE", GRA_Mode }, { "AVERAGE", GRA_Average }, }; static void DumpStringList(char **papszStrList) { if (papszStrList == NULL) return ; while( *papszStrList != NULL ) { CPLDebug("DumpStringList",": '%s'", *papszStrList); ++papszStrList; } return ; } GDALDataset* OGRDB2DataSource::CreateCopy( const char *pszFilename, GDALDataset *poSrcDS, int bStrict, char ** papszOptions, GDALProgressFunc pfnProgress, void * pProgressData ) { CPLDebug("OGRDB2DataSource::CreateCopy","pszFilename: '%s'", pszFilename); CPLDebug("OGRDB2DataSource::CreateCopy","srcDescription: '%s'", poSrcDS->GetDescription()); const char* pszTilingScheme = CSLFetchNameValueDef(papszOptions, "TILING_SCHEME", "CUSTOM"); DumpStringList(papszOptions); char** papszUpdatedOptions = CSLDuplicate(papszOptions); if( CSLFetchNameValue(papszOptions, "RASTER_TABLE") == NULL ) { papszUpdatedOptions = CSLSetNameValue(papszUpdatedOptions, "RASTER_TABLE", CPLGetBasename(poSrcDS->GetDescription())); } DumpStringList(papszUpdatedOptions); if( EQUAL(pszTilingScheme, "CUSTOM") ) { GDALDriver* poThisDriver = (GDALDriver*)GDALGetDriverByName("DB2ODBC"); if( !poThisDriver ) { CSLDestroy(papszUpdatedOptions); return NULL; } CPLDebug("OGRDB2DataSource::CreateCopy","calling DefaultCreateCopy"); GDALDataset* poDS = poThisDriver->DefaultCreateCopy( pszFilename, poSrcDS, bStrict, papszUpdatedOptions, pfnProgress, pProgressData ); CPLDebug("OGRDB2DataSource::CreateCopy","returned from DefaultCreateCopy"); CSLDestroy(papszUpdatedOptions); return poDS; } int nBands = poSrcDS->GetRasterCount(); if( nBands != 1 && nBands != 2 && nBands != 3 && nBands != 4 ) { CPLError(CE_Failure, CPLE_NotSupported, "Only 1 (Grey/ColorTable), 2 (Grey+Alpha), 3 (RGB) or 4 (RGBA) band dataset supported"); CSLDestroy(papszUpdatedOptions); return NULL; } int bFound = FALSE; int nEPSGCode = 0; size_t iScheme; for(iScheme = 0; iScheme < sizeof(asTilingShemes)/sizeof(asTilingShemes[0]); iScheme++ ) { if( EQUAL(pszTilingScheme, asTilingShemes[iScheme].pszName) ) { nEPSGCode = asTilingShemes[iScheme].nEPSGCode; bFound = TRUE; break; } } if( !bFound ) { CSLDestroy(papszUpdatedOptions); return NULL; } OGRSpatialReference oSRS; if( oSRS.importFromEPSG(nEPSGCode) != OGRERR_NONE ) { CSLDestroy(papszUpdatedOptions); return NULL; } char* pszWKT = NULL; oSRS.exportToWkt(&pszWKT); char** papszTO = CSLSetNameValue( NULL, "DST_SRS", pszWKT ); void* hTransformArg = GDALCreateGenImgProjTransformer2( poSrcDS, NULL, papszTO ); if( hTransformArg == NULL ) { CSLDestroy(papszUpdatedOptions); CPLFree(pszWKT); CSLDestroy(papszTO); return NULL; } GDALTransformerInfo* psInfo = (GDALTransformerInfo*)hTransformArg; double adfGeoTransform[6]; double adfExtent[4]; int nXSize, nYSize; if ( GDALSuggestedWarpOutput2( poSrcDS, psInfo->pfnTransform, hTransformArg, adfGeoTransform, &nXSize, &nYSize, adfExtent, 0 ) != CE_None ) { CSLDestroy(papszUpdatedOptions); CPLFree(pszWKT); CSLDestroy(papszTO); GDALDestroyGenImgProjTransformer( hTransformArg ); return NULL; } GDALDestroyGenImgProjTransformer( hTransformArg ); hTransformArg = NULL; int nZoomLevel; double dfComputedRes = adfGeoTransform[1]; double dfPrevRes = 0, dfRes = 0; for(nZoomLevel = 0; nZoomLevel < 25; nZoomLevel++) { dfRes = asTilingShemes[iScheme].dfPixelXSizeZoomLevel0 / (1 << nZoomLevel); if( dfComputedRes > dfRes ) break; dfPrevRes = dfRes; } if( nZoomLevel == 25 ) { CPLError(CE_Failure, CPLE_AppDefined, "Could not find an appropriate zoom level"); CSLDestroy(papszUpdatedOptions); CPLFree(pszWKT); CSLDestroy(papszTO); return NULL; } const char* pszZoomLevelStrategy = CSLFetchNameValueDef(papszOptions, "ZOOM_LEVEL_STRATEGY", "AUTO"); if( fabs( dfComputedRes - dfRes ) / dfRes > 1e-8 ) { if( EQUAL(pszZoomLevelStrategy, "LOWER") ) { if( nZoomLevel > 0 ) nZoomLevel --; } else if( EQUAL(pszZoomLevelStrategy, "UPPER") ) { /* do nothing */ } else if( nZoomLevel > 0 ) { if( dfPrevRes / dfComputedRes < dfComputedRes / dfRes ) nZoomLevel --; } } dfRes = asTilingShemes[iScheme].dfPixelXSizeZoomLevel0 / (1 << nZoomLevel); double dfMinX = adfExtent[0]; double dfMinY = adfExtent[1]; double dfMaxX = adfExtent[2]; double dfMaxY = adfExtent[3]; nXSize = (int) ( 0.5 + ( dfMaxX - dfMinX ) / dfRes ); nYSize = (int) ( 0.5 + ( dfMaxY - dfMinY ) / dfRes ); adfGeoTransform[1] = dfRes; adfGeoTransform[5] = -dfRes; int nTargetBands = nBands; /* For grey level or RGB, if there's reprojection involved, add an alpha */ /* channel */ if( (nBands == 1 && poSrcDS->GetRasterBand(1)->GetColorTable() == NULL) || nBands == 3 ) { OGRSpatialReference oSrcSRS; oSrcSRS.SetFromUserInput(poSrcDS->GetProjectionRef()); oSrcSRS.AutoIdentifyEPSG(); if( oSrcSRS.GetAuthorityCode(NULL) == NULL || atoi(oSrcSRS.GetAuthorityCode(NULL)) != nEPSGCode ) { nTargetBands ++; } } OGRDB2DataSource* poDS = new OGRDB2DataSource(); if( !(poDS->Create( pszFilename, nXSize, nYSize, nTargetBands, GDT_Byte, papszUpdatedOptions )) ) { delete poDS; CSLDestroy(papszUpdatedOptions); CPLFree(pszWKT); CSLDestroy(papszTO); return NULL; } CSLDestroy(papszUpdatedOptions); papszUpdatedOptions = NULL; poDS->SetGeoTransform(adfGeoTransform); poDS->SetProjection(pszWKT); CPLFree(pszWKT); pszWKT = NULL; hTransformArg = GDALCreateGenImgProjTransformer2( poSrcDS, poDS, papszTO ); CSLDestroy(papszTO); if( hTransformArg == NULL ) { delete poDS; return NULL; } /* -------------------------------------------------------------------- */ /* Warp the transformer with a linear approximator */ /* -------------------------------------------------------------------- */ hTransformArg = GDALCreateApproxTransformer( GDALGenImgProjTransform, hTransformArg, 0.125 ); GDALApproxTransformerOwnsSubtransformer(hTransformArg, TRUE); /* -------------------------------------------------------------------- */ /* Setup warp options. */ /* -------------------------------------------------------------------- */ GDALWarpOptions *psWO = GDALCreateWarpOptions(); psWO->papszWarpOptions = NULL; psWO->eWorkingDataType = GDT_Byte; GDALResampleAlg eResampleAlg = GRA_Bilinear; const char* pszResampling = CSLFetchNameValue(papszOptions, "RESAMPLING"); if( pszResampling ) { for(size_t iAlg = 0; iAlg < sizeof(asResamplingAlg)/sizeof(asResamplingAlg[0]); iAlg ++) { if( EQUAL(pszResampling, asResamplingAlg[iAlg].pszName) ) { eResampleAlg = asResamplingAlg[iAlg].eResampleAlg; break; } } } psWO->eResampleAlg = eResampleAlg; psWO->hSrcDS = poSrcDS; psWO->hDstDS = poDS; psWO->pfnTransformer = GDALApproxTransform; psWO->pTransformerArg = hTransformArg; psWO->pfnProgress = pfnProgress; psWO->pProgressArg = pProgressData; /* -------------------------------------------------------------------- */ /* Setup band mapping. */ /* -------------------------------------------------------------------- */ if( nBands == 2 || nBands == 4 ) psWO->nBandCount = nBands - 1; else psWO->nBandCount = nBands; psWO->panSrcBands = (int *) CPLMalloc(psWO->nBandCount*sizeof(int)); psWO->panDstBands = (int *) CPLMalloc(psWO->nBandCount*sizeof(int)); for( int i = 0; i < psWO->nBandCount; i++ ) { psWO->panSrcBands[i] = i+1; psWO->panDstBands[i] = i+1; } if( nBands == 2 || nBands == 4 ) { psWO->nSrcAlphaBand = nBands; } if( nTargetBands == 2 || nTargetBands == 4 ) { psWO->nDstAlphaBand = nTargetBands; } /* -------------------------------------------------------------------- */ /* Initialize and execute the warp. */ /* -------------------------------------------------------------------- */ GDALWarpOperation oWO; CPLErr eErr = oWO.Initialize( psWO ); if( eErr == CE_None ) { /*if( bMulti ) eErr = oWO.ChunkAndWarpMulti( 0, 0, nXSize, nYSize ); else*/ eErr = oWO.ChunkAndWarpImage( 0, 0, nXSize, nYSize ); } if (eErr != CE_None) { delete poDS; poDS = NULL; } GDALDestroyTransformer( hTransformArg ); GDALDestroyWarpOptions( psWO ); return poDS; } /************************************************************************/ /* GetProjectionRef() */ /************************************************************************/ const char* OGRDB2DataSource::GetProjectionRef() { return (m_pszProjection) ? m_pszProjection : ""; } /************************************************************************/ /* SetProjection() */ /************************************************************************/ CPLErr OGRDB2DataSource::SetProjection( const char* pszProjection ) { CPLDebug("OGRDB2DataSource::SetProjection", "pszProjection: '%s'", pszProjection); if( nBands == 0) { CPLError(CE_Failure, CPLE_NotSupported, "SetProjection() not supported on a dataset with 0 band"); return CE_Failure; } if( eAccess != GA_Update ) { CPLError(CE_Failure, CPLE_NotSupported, "SetProjection() not supported on read-only dataset"); return CE_Failure; } int nSRID; if( pszProjection == NULL || pszProjection[0] == '\0' ) { nSRID = -1; } else { OGRSpatialReference oSRS; if( oSRS.SetFromUserInput(pszProjection) != OGRERR_NONE ) return CE_Failure; nSRID = FetchSRSId( &oSRS ); } for(size_t iScheme = 0; iScheme < sizeof(asTilingShemes)/sizeof(asTilingShemes[0]); iScheme++ ) { if( EQUAL(m_osTilingScheme, asTilingShemes[iScheme].pszName) ) { if( nSRID != asTilingShemes[iScheme].nEPSGCode ) { CPLError(CE_Failure, CPLE_NotSupported, "Projection should be EPSG:%d for %s tiling scheme", asTilingShemes[iScheme].nEPSGCode, m_osTilingScheme.c_str()); return CE_Failure; } } } m_nSRID = nSRID; CPLFree(m_pszProjection); m_pszProjection = pszProjection ? CPLStrdup(pszProjection): CPLStrdup(""); if( m_bRecordInsertedInGPKGContent ) { OGRDB2Statement oStatement( GetSession() ); oStatement.Appendf( "UPDATE gpkg.contents SET srs_id = %d " "WHERE table_name = '%s'", m_nSRID, m_osRasterTable.c_str()); if( !oStatement.DB2Execute("OGRDB2DataSource::SetProjection") ) { CPLError(CE_Failure, CPLE_AppDefined, "Set projection failed in gpkg.contents " "for table %s: %s", m_osRasterTable.c_str(), GetSession()->GetLastError()); CPLDebug("OGRDB2DataSource::SetProjection", "Set projection failed in gpkg.contents " "for table %s: %s", m_osRasterTable.c_str(), GetSession()->GetLastError()); return CE_Failure; } oStatement.Clear(); oStatement.Appendf( "UPDATE gpkg.tile_matrix_set SET srs_id = %d " "WHERE table_name = '%s'", m_nSRID, m_osRasterTable.c_str()); if( !oStatement.DB2Execute("OGRDB2DataSource::SetProjection") ) { CPLError(CE_Failure, CPLE_AppDefined, "Set projection in gpkg.tile_matrix_set failed " "for table %s: %s", m_osRasterTable.c_str(), GetSession()->GetLastError()); CPLDebug("OGRDB2DataSource::SetProjection", "Set projection in gpkg.tile_matrix_set failed " "for table %s: %s", m_osRasterTable.c_str(), GetSession()->GetLastError()); return CE_Failure; } } return CE_None; } /************************************************************************/ /* GetGeoTransform() */ /************************************************************************/ CPLErr OGRDB2DataSource::GetGeoTransform( double* padfGeoTransform ) { memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double)); if( !m_bGeoTransformValid ) return CE_Failure; else return CE_None; } /************************************************************************/ /* SetGeoTransform() */ /************************************************************************/ CPLErr OGRDB2DataSource::SetGeoTransform( double* padfGeoTransform ) { if( nBands == 0) { CPLError(CE_Failure, CPLE_NotSupported, "SetGeoTransform() not supported on a dataset with 0 band"); return CE_Failure; } if( eAccess != GA_Update ) { CPLError(CE_Failure, CPLE_NotSupported, "SetGeoTransform() not supported on read-only dataset"); return CE_Failure; } if( m_bGeoTransformValid ) { CPLError(CE_Failure, CPLE_NotSupported, "Cannot modify geotransform once set"); return CE_Failure; } if( padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0 || padfGeoTransform[5] > 0.0 ) { CPLError(CE_Failure, CPLE_NotSupported, "Only north-up non rotated geotransform supported"); return CE_Failure; } for(size_t iScheme = 0; iScheme < sizeof(asTilingShemes)/sizeof(asTilingShemes[0]); iScheme++ ) { if( EQUAL(m_osTilingScheme, asTilingShemes[iScheme].pszName) ) { double dfPixelXSizeZoomLevel0 = asTilingShemes[iScheme].dfPixelXSizeZoomLevel0; double dfPixelYSizeZoomLevel0 = asTilingShemes[iScheme].dfPixelYSizeZoomLevel0; for( m_nZoomLevel = 0; m_nZoomLevel < 25; m_nZoomLevel++ ) { double dfExpectedPixelXSize = dfPixelXSizeZoomLevel0 / (1 << m_nZoomLevel); double dfExpectedPixelYSize = dfPixelYSizeZoomLevel0 / (1 << m_nZoomLevel); if( fabs( padfGeoTransform[1] - dfExpectedPixelXSize ) < 1e-8 * dfExpectedPixelXSize && fabs( fabs(padfGeoTransform[5]) - dfExpectedPixelYSize ) < 1e-8 * dfExpectedPixelYSize ) { break; } } if( m_nZoomLevel == 25 ) { m_nZoomLevel = -1; CPLError(CE_Failure, CPLE_NotSupported, "Could not find an appropriate zoom level of %s tiling scheme that matches raster pixel size", m_osTilingScheme.c_str()); return CE_Failure; } break; } } memcpy(m_adfGeoTransform, padfGeoTransform, 6 * sizeof(double)); m_bGeoTransformValid = TRUE; return FinalizeRasterRegistration(); } /************************************************************************/ /* FinalizeRasterRegistration() */ /************************************************************************/ CPLErr OGRDB2DataSource::FinalizeRasterRegistration() { CPLDebug("OGRDB2DataSource::FinalizeRasterRegistration","Entering"); m_dfTMSMinX = m_adfGeoTransform[0]; m_dfTMSMaxY = m_adfGeoTransform[3]; int nTileWidth, nTileHeight; GetRasterBand(1)->GetBlockSize(&nTileWidth, &nTileHeight); m_nTileMatrixWidth = (nRasterXSize + nTileWidth - 1) / nTileWidth; m_nTileMatrixHeight = (nRasterYSize + nTileHeight - 1) / nTileHeight; CPLDebug("OGRDB2DataSource::FinalizeRasterRegistration", "m_nZoomLevel: %d; nTileWidth: %d; nTileHeight %d", m_nZoomLevel, nTileWidth, nTileHeight); CPLDebug("OGRDB2DataSource::FinalizeRasterRegistration", "nRasterXSize: %d; nRasterYSize %d", nRasterXSize, nRasterYSize); if( m_nZoomLevel < 0 ) { m_nZoomLevel = 0; while( (nRasterXSize >> m_nZoomLevel) > nTileWidth || (nRasterYSize >> m_nZoomLevel) > nTileHeight ) m_nZoomLevel ++; } double dfPixelXSizeZoomLevel0 = m_adfGeoTransform[1] * (1 << m_nZoomLevel); double dfPixelYSizeZoomLevel0 = fabs(m_adfGeoTransform[5]) * (1 << m_nZoomLevel); int nTileXCountZoomLevel0 = ((nRasterXSize >> m_nZoomLevel) + nTileWidth - 1) / nTileWidth; int nTileYCountZoomLevel0 = ((nRasterYSize >> m_nZoomLevel) + nTileHeight - 1) / nTileHeight; for(size_t iScheme = 0; iScheme < sizeof(asTilingShemes)/sizeof(asTilingShemes[0]); iScheme++ ) { if( EQUAL(m_osTilingScheme, asTilingShemes[iScheme].pszName) ) { CPLAssert( m_nZoomLevel >= 0 ); m_dfTMSMinX = asTilingShemes[iScheme].dfMinX; m_dfTMSMaxY = asTilingShemes[iScheme].dfMaxY; dfPixelXSizeZoomLevel0 = asTilingShemes[iScheme].dfPixelXSizeZoomLevel0; dfPixelYSizeZoomLevel0 = asTilingShemes[iScheme].dfPixelYSizeZoomLevel0; nTileXCountZoomLevel0 = asTilingShemes[iScheme].nTileXCountZoomLevel0; nTileYCountZoomLevel0 = asTilingShemes[iScheme].nTileYCountZoomLevel0; m_nTileMatrixWidth = nTileXCountZoomLevel0 * (1 << m_nZoomLevel); m_nTileMatrixHeight = nTileYCountZoomLevel0 * (1 << m_nZoomLevel); break; } } ComputeTileAndPixelShifts(); double dfGDALMinX = m_adfGeoTransform[0]; double dfGDALMinY = m_adfGeoTransform[3] + nRasterYSize * m_adfGeoTransform[5]; double dfGDALMaxX = m_adfGeoTransform[0] + nRasterXSize * m_adfGeoTransform[1]; double dfGDALMaxY = m_adfGeoTransform[3]; OGRDB2Statement oStatement( GetSession() ); oStatement.Appendf( "INSERT INTO gpkg.contents " "(table_name,data_type,identifier,description,min_x,min_y,max_x,max_y,srs_id) VALUES " "('%s','tiles','%s','%s',%.18g,%.18g,%.18g,%.18g,%d)", m_osRasterTable.c_str(), m_osIdentifier.c_str(), m_osDescription.c_str(), dfGDALMinX, dfGDALMinY, dfGDALMaxX, dfGDALMaxY, m_nSRID); if( !oStatement.DB2Execute("OGRDB2DataSource::FinalizeRasterRegistration") ) { CPLError(CE_Failure, CPLE_AppDefined, "Insert into gpkg.contents failed"); CPLDebug("OGRDB2DataSource::FinalizeRasterRegistration", "Insert into gpkg.contents failed;" "error: %s; ", GetSession()->GetLastError()); return CE_Failure; } double dfTMSMaxX = m_dfTMSMinX + nTileXCountZoomLevel0 * nTileWidth * dfPixelXSizeZoomLevel0; double dfTMSMinY = m_dfTMSMaxY - nTileYCountZoomLevel0 * nTileHeight * dfPixelYSizeZoomLevel0; oStatement.Clear(); oStatement.Appendf( "INSERT INTO gpkg.tile_matrix_set " "(table_name,srs_id,min_x,min_y,max_x,max_y) VALUES " "('%s',%d,%.18g,%.18g,%.18g,%.18g)", m_osRasterTable.c_str(), m_nSRID, m_dfTMSMinX,dfTMSMinY,dfTMSMaxX,m_dfTMSMaxY); if( !oStatement.DB2Execute("OGRDB2DataSource::FinalizeRasterRegistration") ) { CPLError(CE_Failure, CPLE_AppDefined, "Insert into gpkg.tile_matrix_set failed"); CPLDebug("OGRDB2DataSource::FinalizeRasterRegistration", "Insert into gpkg.tile_matrix_set failed;" "error: %s; ", GetSession()->GetLastError()); return CE_Failure; } m_papoOverviewDS = (OGRDB2DataSource**) CPLCalloc(sizeof(OGRDB2DataSource*), m_nZoomLevel); CPLDebug("OGRDB2DataSource::FinalizeRasterRegistration", "m_nZoomLevel: %d", m_nZoomLevel); for(int i=0; i<=m_nZoomLevel; i++) { double dfPixelXSizeZoomLevel, dfPixelYSizeZoomLevel; int nTileMatrixWidth, nTileMatrixHeight; if( EQUAL(m_osTilingScheme, "CUSTOM") ) { dfPixelXSizeZoomLevel = m_adfGeoTransform[1] * (1 << (m_nZoomLevel-i)); dfPixelYSizeZoomLevel = fabs(m_adfGeoTransform[5]) * (1 << (m_nZoomLevel-i)); nTileMatrixWidth = ((nRasterXSize >> (m_nZoomLevel-i)) + nTileWidth - 1) / nTileWidth; nTileMatrixHeight = ((nRasterYSize >> (m_nZoomLevel-i)) + nTileHeight - 1) / nTileHeight; } else { dfPixelXSizeZoomLevel = dfPixelXSizeZoomLevel0 / (1 << i); dfPixelYSizeZoomLevel = dfPixelYSizeZoomLevel0 / (1 << i); nTileMatrixWidth = nTileXCountZoomLevel0 * (1 << i); nTileMatrixHeight = nTileYCountZoomLevel0 * (1 << i); } oStatement.Clear(); oStatement.Appendf( "INSERT INTO gpkg.tile_matrix " "(table_name,zoom_level,matrix_width,matrix_height, " "tile_width,tile_height,pixel_x_size,pixel_y_size) " "VALUES " "('%s',%d,%d,%d,%d,%d,%.18g,%.18g)", m_osRasterTable.c_str(),i,nTileMatrixWidth, nTileMatrixHeight, nTileWidth,nTileHeight,dfPixelXSizeZoomLevel, dfPixelYSizeZoomLevel); if( !oStatement.DB2Execute("OGRDB2DataSource::FinalizeRasterRegistration") ) { CPLError(CE_Failure, CPLE_AppDefined, "Insert into gpkg.tile_matrix_set failed"); CPLDebug("OGRDB2DataSource::FinalizeRasterRegistration", "Insert into gpkg.tile_matrix_set failed;" "error: %s; ", GetSession()->GetLastError()); return CE_Failure; } if( i < m_nZoomLevel ) { OGRDB2DataSource* poOvrDS = new OGRDB2DataSource(); poOvrDS->InitRaster ( this, m_osRasterTable, i, nBands, m_dfTMSMinX, m_dfTMSMaxY, dfPixelXSizeZoomLevel, dfPixelYSizeZoomLevel, nTileWidth, nTileHeight, nTileMatrixWidth,nTileMatrixHeight, dfGDALMinX, dfGDALMinY, dfGDALMaxX, dfGDALMaxY ); m_papoOverviewDS[m_nZoomLevel-1-i] = poOvrDS; } } SoftCommitTransaction(); m_nOverviewCount = m_nZoomLevel; m_bRecordInsertedInGPKGContent = TRUE; return CE_None; } /************************************************************************/ /* IBuildOverviews() */ /************************************************************************/ static int GetFloorPowerOfTwo(int n) { int p2 = 1; while( (n = n >> 1) > 0 ) { p2 <<= 1; } return p2; } CPLErr OGRDB2DataSource::IBuildOverviews( const char * pszResampling, int nOverviews, int * panOverviewList, int nBandsIn, CPL_UNUSED int * panBandList, GDALProgressFunc pfnProgress, void * pProgressData ) { CPLDebug("OGRDB2DataSource::IBuildOverviews", "nOverviews: %d; m_nOverviewCount: %d", nOverviews, m_nOverviewCount); if( GetAccess() != GA_Update ) { CPLError(CE_Failure, CPLE_NotSupported, "Overview building not supported on a database opened in read-only mode"); return CE_Failure; } if( m_poParentDS != NULL ) { CPLError(CE_Failure, CPLE_NotSupported, "Overview building not supported on overview dataset"); return CE_Failure; } OGRDB2Statement oStatement( &m_oSession ); if( nOverviews == 0 ) { for(int i=0; i<m_nOverviewCount; i++) m_papoOverviewDS[i]->FlushCache(); oStatement.Appendf("DELETE FROM %s WHERE zoom_level < %d", m_osRasterTable.c_str(), m_nZoomLevel); #ifdef DEBUG_SQL CPLDebug("OGRDB2DataSource::IBuildOverviews", "stmt: '%s'",oStatement.GetCommand()); #endif if( !oStatement.ExecuteSQL() ) { CPLDebug("OGRDB2DataSource::IBuildOverviews", "DELETE failed: %s", GetSession()->GetLastError() ); CPLError( CE_Failure, CPLE_AppDefined, "Delete of overviews failed: %s", GetSession()->GetLastError() ); return CE_Failure; } return CE_None; } if( nBandsIn != nBands ) { CPLError( CE_Failure, CPLE_NotSupported, "Generation of overviews in GPKG only" "supported when operating on all bands." ); return CE_Failure; } if( m_nOverviewCount == 0 ) { CPLError(CE_Failure, CPLE_AppDefined, "Image too small to support overviews"); return CE_Failure; } FlushCache(); for(int i=0; i<nOverviews; i++) { if( panOverviewList[i] < 2 ) { CPLError(CE_Failure, CPLE_IllegalArg, "Overview factor must be >= 2"); return CE_Failure; } int bFound = FALSE; int jCandidate = -1; int nMaxOvFactor = 0; for(int j=0; j<m_nOverviewCount; j++) { int nOvFactor; GDALDataset* poODS = m_papoOverviewDS[j]; nOvFactor = (int) (0.5 + GetRasterXSize() / (double) poODS->GetRasterXSize()); nMaxOvFactor = nOvFactor; if( nOvFactor == panOverviewList[i] || nOvFactor == GDALOvLevelAdjust2( panOverviewList[i], GetRasterXSize(), GetRasterYSize() ) ) { bFound = TRUE; break; } if( jCandidate < 0 && nOvFactor > panOverviewList[i] ) jCandidate = j; } if( !bFound ) { /* Mostly for debug */ if( !CPLTestBool(CPLGetConfigOption("ALLOW_GPKG_ZOOM_OTHER_EXTENSION", "YES")) ) { CPLString osOvrList; for(int j=0; j<m_nOverviewCount; j++) { int nOvFactor; GDALDataset* poODS = m_papoOverviewDS[j]; /* Compute overview factor */ nOvFactor = (int) (0.5 + GetRasterXSize() / (double) poODS->GetRasterXSize()); int nODSXSize = (int)(0.5 + GetRasterXSize() / (double) nOvFactor); if( nODSXSize != poODS->GetRasterXSize() ) { int nOvFactorPowerOfTwo = GetFloorPowerOfTwo(nOvFactor); nODSXSize = (int)(0.5 + GetRasterXSize() / (double) nOvFactorPowerOfTwo); if( nODSXSize == poODS->GetRasterXSize() ) nOvFactor = nOvFactorPowerOfTwo; else { nOvFactorPowerOfTwo <<= 1; nODSXSize = (int)(0.5 + GetRasterXSize() / (double) nOvFactorPowerOfTwo); if( nODSXSize == poODS->GetRasterXSize() ) nOvFactor = nOvFactorPowerOfTwo; } } if( j != 0 ) osOvrList += " "; osOvrList += CPLSPrintf("%d", nOvFactor); } CPLError(CE_Failure, CPLE_NotSupported, "Only overviews %s can be computed", osOvrList.c_str()); return CE_Failure; } else { int nOvFactor = panOverviewList[i]; if( jCandidate < 0 ) jCandidate = m_nOverviewCount; int nOvXSize = GetRasterXSize() / nOvFactor; int nOvYSize = GetRasterYSize() / nOvFactor; if( nOvXSize < 8 || nOvYSize < 8) { CPLError(CE_Failure, CPLE_NotSupported, "Too big overview factor : %d. Would result in a %dx%d overview", nOvFactor, nOvXSize, nOvYSize); return CE_Failure; } if( !(jCandidate == m_nOverviewCount && nOvFactor == 2 * nMaxOvFactor) && !m_bZoomOther ) { CPLError(CE_Warning, CPLE_AppDefined, "Use of overview factor %d cause gpkg_zoom_other extension to be needed", nOvFactor); RegisterZoomOtherExtension(); m_bZoomOther = TRUE; } SoftStartTransaction(); CPLAssert(jCandidate > 0); int nNewZoomLevel = m_papoOverviewDS[jCandidate-1]->m_nZoomLevel; for(int k=0; k<=jCandidate; k++) { oStatement.Appendf( "UPDATE gpkg.tile_matrix SET zoom_level = %d " "WHERE table_name = %s AND zoom_level = %d", m_nZoomLevel - k + 1, m_osRasterTable.c_str(), m_nZoomLevel - k); #ifdef DEBUG_SQL CPLDebug("OGRDB2DataSource::IBuildOverviews", "stmt: '%s'", oStatement.GetCommand()); #endif if( !oStatement.ExecuteSQL() ) { SoftRollbackTransaction(); CPLError(CE_Failure, CPLE_AppDefined, "updating tile_matrix failed " "for table %s: %s ", m_osRasterTable.c_str(), GetSession()->GetLastError()); CPLDebug("OGRDB2DataSource::IBuildOverviews", "updating tile_matrix failed " "for table %s: %s ", m_osRasterTable.c_str(), GetSession()->GetLastError()); return CE_Failure; } oStatement.Clear(); oStatement.Appendf( "UPDATE %s SET zoom_level = %d " "WHERE zoom_level = %d", m_osRasterTable.c_str(), m_nZoomLevel - k + 1, m_nZoomLevel - k); #ifdef DEBUG_SQL CPLDebug("OGRDB2DataSource::IBuildOverviews", "stmt: '%s'", oStatement.GetCommand()); #endif if( !oStatement.ExecuteSQL() ) { SoftRollbackTransaction(); CPLError(CE_Failure, CPLE_AppDefined, "update failed " "for table %s: %s ", m_osRasterTable.c_str(), GetSession()->GetLastError()); CPLDebug("OGRDB2DataSource::IBuildOverviews", "update failed " "for table %s: %s ", m_osRasterTable.c_str(), GetSession()->GetLastError()); return CE_Failure; } } double dfGDALMinX = m_adfGeoTransform[0]; double dfGDALMinY = m_adfGeoTransform[3] + nRasterYSize * m_adfGeoTransform[5]; double dfGDALMaxX = m_adfGeoTransform[0] + nRasterXSize * m_adfGeoTransform[1]; double dfGDALMaxY = m_adfGeoTransform[3]; double dfPixelXSizeZoomLevel = m_adfGeoTransform[1] * nOvFactor; double dfPixelYSizeZoomLevel = fabs(m_adfGeoTransform[5]) * nOvFactor; int nTileWidth, nTileHeight; GetRasterBand(1)->GetBlockSize(&nTileWidth, &nTileHeight); int nTileMatrixWidth = (nOvXSize + nTileWidth - 1) / nTileWidth; int nTileMatrixHeight = (nOvYSize + nTileHeight - 1) / nTileHeight; oStatement.Clear(); oStatement.Appendf( "INSERT INTO gpkg.tile_matrix " "(table_name,zoom_level,matrix_width,matrix_height,tile_width,tile_height,pixel_x_size,pixel_y_size) VALUES " "(%s,%d,%d,%d,%d,%d,%.18g,%.18g)", m_osRasterTable.c_str(),nNewZoomLevel,nTileMatrixWidth,nTileMatrixHeight, nTileWidth,nTileHeight,dfPixelXSizeZoomLevel,dfPixelYSizeZoomLevel); #ifdef DEBUG_SQL CPLDebug("OGRDB2DataSource::IBuildOverviews", "stmt: '%s'", oStatement.GetCommand()); #endif if( !oStatement.ExecuteSQL() ) { SoftRollbackTransaction(); CPLError(CE_Failure, CPLE_AppDefined, "insert into tile_matrix failed " "for table %s: %s ", m_osRasterTable.c_str(), GetSession()->GetLastError()); CPLDebug("OGRDB2DataSource::IBuildOverviews", "insert into tile_matrix failed " "for table %s: %s ", m_osRasterTable.c_str(), GetSession()->GetLastError()); return CE_Failure; } SoftCommitTransaction(); m_nZoomLevel ++; /* this change our zoom level as well as previous overviews */ for(int k=0; k<jCandidate; k++) m_papoOverviewDS[k]->m_nZoomLevel ++; OGRDB2DataSource* poOvrDS = new OGRDB2DataSource(); poOvrDS->InitRaster ( this, m_osRasterTable, nNewZoomLevel, nBands, m_dfTMSMinX, m_dfTMSMaxY, dfPixelXSizeZoomLevel, dfPixelYSizeZoomLevel, nTileWidth, nTileHeight, nTileMatrixWidth,nTileMatrixHeight, dfGDALMinX, dfGDALMinY, dfGDALMaxX, dfGDALMaxY ); m_papoOverviewDS = (OGRDB2DataSource**) CPLRealloc( m_papoOverviewDS, sizeof(OGRDB2DataSource*) * (m_nOverviewCount+1)); if( jCandidate < m_nOverviewCount ) { memmove(m_papoOverviewDS + jCandidate + 1, m_papoOverviewDS + jCandidate, sizeof(OGRDB2DataSource*) * (m_nOverviewCount-jCandidate)); } m_papoOverviewDS[jCandidate] = poOvrDS; m_nOverviewCount ++; } } } GDALRasterBand*** papapoOverviewBands = (GDALRasterBand ***) CPLCalloc(sizeof(void*),nBands); for( int iBand = 0; iBand < nBands; iBand++ ) { papapoOverviewBands[iBand] = (GDALRasterBand **) CPLCalloc(sizeof(void*),nOverviews); int iCurOverview = 0; for(int i=0; i<nOverviews; i++) { int j; for( j = 0; j < m_nOverviewCount; j++ ) { int nOvFactor; GDALDataset* poODS = m_papoOverviewDS[j]; nOvFactor = GDALComputeOvFactor(poODS->GetRasterXSize(), GetRasterXSize(), poODS->GetRasterYSize(), GetRasterYSize()); if( nOvFactor == panOverviewList[i] || nOvFactor == GDALOvLevelAdjust2( panOverviewList[i], GetRasterXSize(), GetRasterYSize() ) ) { papapoOverviewBands[iBand][iCurOverview] = poODS->GetRasterBand(iBand+1); iCurOverview++ ; break; } } CPLAssert(j < m_nOverviewCount); } CPLAssert(iCurOverview == nOverviews); } CPLErr eErr = GDALRegenerateOverviewsMultiBand(nBands, papoBands, nOverviews, papapoOverviewBands, pszResampling, pfnProgress, pProgressData ); for( int iBand = 0; iBand < nBands; iBand++ ) { CPLFree(papapoOverviewBands[iBand]); } CPLFree(papapoOverviewBands); return eErr; } /************************************************************************/ /* RegisterZoomOtherExtension() */ /************************************************************************/ int OGRDB2DataSource::RegisterZoomOtherExtension() { CPLDebug("OGRDB2DataSource::RegisterZoomOtherExtension", "NO-OP"); CreateExtensionsTableIfNecessary(); #ifdef LATER char* pszSQL = sqlite3_mprintf( "INSERT INTO gpkg_extensions " "(table_name, extension_name, definition, scope) " "VALUES " "('%q', 'gpkg_zoom_other', 'GeoPackage 1.0 Specification Annex O', 'read-write')", m_osRasterTable.c_str()); OGRErr eErr = SQLCommand(hDB, pszSQL); sqlite3_free(pszSQL); if ( OGRERR_NONE != eErr ) return FALSE; #endif return TRUE; } /************************************************************************/ /* CreateGDALAspatialExtension() */ /************************************************************************/ OGRErr OGRDB2DataSource::CreateGDALAspatialExtension() { CreateExtensionsTableIfNecessary(); CPLDebug("OGRDB2DataSource::CreateGDALAspatialExtension", "NO-OP"); #ifdef LATER if( HasGDALAspatialExtension() ) return OGRERR_NONE; const char* pszCreateAspatialExtension = "INSERT INTO gpkg_extensions " "(table_name, column_name, extension_name, definition, scope) " "VALUES " "(NULL, NULL, 'gdal_aspatial', 'http://gdal.org/geopackage_aspatial.html', 'read-write')"; return SQLCommand(hDB, pszCreateAspatialExtension); #endif return OGRERR_NONE; }