LCOV - code coverage report
Current view: directory - ogr/ogrsf_frmts/vfk - vfkdatablocksqlite.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 444 336 75.7 %
Date: 2013-03-30 Functions: 14 13 92.9 %

       1                 : /******************************************************************************
       2                 :  * $Id: vfkdatablocksqlite.cpp 25721 2013-03-09 16:21:46Z martinl $
       3                 :  *
       4                 :  * Project:  VFK Reader - Data block definition (SQLite)
       5                 :  * Purpose:  Implements VFKDataBlockSQLite
       6                 :  * Author:   Martin Landa, landa.martin gmail.com
       7                 :  *
       8                 :  ******************************************************************************
       9                 :  * Copyright (c) 2012-2013, Martin Landa <landa.martin gmail.com>
      10                 :  *
      11                 :  * Permission is hereby granted, free of charge, to any person
      12                 :  * obtaining a copy of this software and associated documentation
      13                 :  * files (the "Software"), to deal in the Software without
      14                 :  * restriction, including without limitation the rights to use, copy,
      15                 :  * modify, merge, publish, distribute, sublicense, and/or sell copies
      16                 :  * of the Software, and to permit persons to whom the Software is
      17                 :  * furnished to do so, subject to the following conditions:
      18                 :  *
      19                 :  * The above copyright notice and this permission notice shall be
      20                 :  * included in all copies or substantial portions of the Software.
      21                 :  *
      22                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
      23                 :  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      24                 :  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
      25                 :  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
      26                 :  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
      27                 :  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
      28                 :  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      29                 :  * SOFTWARE.
      30                 :  ****************************************************************************/
      31                 : 
      32                 : #include <algorithm>
      33                 : 
      34                 : #include "vfkreader.h"
      35                 : #include "vfkreaderp.h"
      36                 : 
      37                 : #include "cpl_conv.h"
      38                 : #include "cpl_error.h"
      39                 : 
      40                 : /*!
      41                 :   \brief Load geometry (point layers)
      42                 : 
      43                 :   \return number of invalid features
      44                 : */
      45               2 : int VFKDataBlockSQLite::LoadGeometryPoint()
      46                 : {
      47                 :     int   nInvalid, rowId, nGeometries;
      48                 :     bool  bSkipInvalid;
      49                 :     long iFID;
      50                 :     double x, y;
      51                 : 
      52               2 :     CPLString     osSQL;
      53                 :     sqlite3_stmt *hStmt;
      54                 :     
      55                 :     VFKFeatureSQLite *poFeature;
      56                 :     VFKReaderSQLite  *poReader;
      57                 :     
      58               2 :     nInvalid  = nGeometries = 0;
      59               2 :     poReader  = (VFKReaderSQLite*) m_poReader;
      60                 : 
      61               2 :     if (LoadGeometryFromDB()) /* try to load geometry from DB */
      62               1 :   return 0;
      63                 :     
      64               1 :     bSkipInvalid = EQUAL(m_pszName, "OB") || EQUAL(m_pszName, "OP") || EQUAL(m_pszName, "OBBP");
      65                 :     osSQL.Printf("SELECT SOURADNICE_Y,SOURADNICE_X,%s,rowid FROM %s",
      66               1 :                  FID_COLUMN, m_pszName);
      67               1 :     hStmt = poReader->PrepareStatement(osSQL.c_str());
      68                 : 
      69               1 :     if (poReader->IsSpatial())
      70               1 :   poReader->ExecuteSQL("BEGIN");
      71                 : 
      72               1 :     while(poReader->ExecuteSQL(hStmt) == OGRERR_NONE) {
      73                 :         /* read values */
      74              13 :         x = -1.0 * sqlite3_column_double(hStmt, 0); /* S-JTSK coordinate system expected */
      75              13 :         y = -1.0 * sqlite3_column_double(hStmt, 1);
      76              13 :   iFID = sqlite3_column_double(hStmt, 2);
      77              13 :   rowId = sqlite3_column_int(hStmt, 3);
      78                 : 
      79              13 :         poFeature = (VFKFeatureSQLite *) GetFeatureByIndex(rowId - 1);
      80              13 :         CPLAssert(NULL != poFeature && poFeature->GetFID() == iFID);
      81                 :         
      82                 :         /* create geometry */
      83              13 :   OGRPoint pt(x, y);
      84              13 :         if (!poFeature->SetGeometry(&pt)) {
      85               0 :             nInvalid++;
      86               0 :             continue;
      87                 :         }
      88                 :         
      89                 :   /* store also geometry in DB */
      90              13 :   if (poReader->IsSpatial() &&
      91                 :       SaveGeometryToDB(&pt, rowId) != OGRERR_FAILURE)
      92              13 :       nGeometries++;
      93                 :     }
      94                 :     
      95                 :     /* update number of geometries in 'vfk_blocks' table */
      96               1 :     UpdateVfkBlocks(nGeometries);
      97                 : 
      98               1 :     if (poReader->IsSpatial())
      99               1 :   poReader->ExecuteSQL("COMMIT");
     100                 :     
     101               1 :     return bSkipInvalid ? 0 : nInvalid;
     102                 : }
     103                 : 
     104                 : /*!
     105                 :   \brief Load geometry (linestring SBP layer)
     106                 : 
     107                 :   \return number of invalid features
     108                 : */
     109               2 : int VFKDataBlockSQLite::LoadGeometryLineStringSBP()
     110                 : {
     111                 :     int      nInvalid, nGeometries, rowId;
     112                 :     long int iFID;
     113                 :     GUIntBig id, ipcb;
     114                 :     bool     bValid;
     115                 :     
     116               2 :     std::vector<int> rowIdFeat;
     117               2 :     CPLString     osSQL;
     118                 :     sqlite3_stmt *hStmt;
     119                 :     
     120                 :     VFKReaderSQLite    *poReader;
     121                 :     VFKDataBlockSQLite *poDataBlockPoints;
     122                 :     VFKFeatureSQLite   *poFeature, *poPoint, *poLine;
     123                 :     
     124               2 :     OGRLineString oOGRLine;
     125                 :     
     126               2 :     nInvalid  = nGeometries = 0;
     127               2 :     poReader  = (VFKReaderSQLite*) m_poReader;
     128               2 :     poLine    = NULL;
     129                 :     
     130               2 :     poDataBlockPoints = (VFKDataBlockSQLite *) m_poReader->GetDataBlock("SOBR");
     131               2 :     if (NULL == poDataBlockPoints) {
     132                 :         CPLError(CE_Failure, CPLE_FileIO, 
     133               0 :                  "Data block %s not found.\n", m_pszName);
     134               0 :         return nInvalid;
     135                 :     }
     136                 :     
     137               2 :     poDataBlockPoints->LoadGeometry();
     138                 :     
     139               2 :     if (LoadGeometryFromDB()) /* try to load geometry from DB */
     140               1 :   return 0;
     141                 : 
     142               1 :     osSQL.Printf("UPDATE %s SET %s = -1", m_pszName, FID_COLUMN);
     143               1 :     poReader->ExecuteSQL(osSQL.c_str());
     144               1 :     bValid = TRUE;
     145               1 :     iFID = 1;
     146               3 :     for (int i = 0; i < 2; i++) {
     147                 :   /* first collect linestrings related to HP, OB or DPM
     148                 :      then collect rest of linestrings */
     149               2 :         if (i == 0)
     150                 :             osSQL.Printf("SELECT BP_ID,PORADOVE_CISLO_BODU,_rowid_ FROM '%s' WHERE "
     151                 :                          "HP_ID IS NOT NULL OR OB_ID IS NOT NULL OR DPM_ID IS NOT NULL "
     152               1 :                          "ORDER BY HP_ID,OB_ID,DPM_ID,PORADOVE_CISLO_BODU", m_pszName);
     153                 :         else
     154                 :             osSQL.Printf("SELECT BP_ID,PORADOVE_CISLO_BODU,_rowid_ FROM '%s' WHERE "
     155                 :                          "OB_ID IS NULL AND HP_ID IS NULL AND DPM_ID IS NULL "
     156               1 :                          "ORDER BY ID,PORADOVE_CISLO_BODU", m_pszName);
     157                 :   
     158               2 :         hStmt = poReader->PrepareStatement(osSQL.c_str());
     159                 :     
     160               2 :   if (poReader->IsSpatial())
     161               2 :       poReader->ExecuteSQL("BEGIN");
     162                 :   
     163              30 :         while(poReader->ExecuteSQL(hStmt) == OGRERR_NONE) {
     164                 :             // read values
     165              26 :             id    = sqlite3_column_double(hStmt, 0);
     166              26 :             ipcb  = sqlite3_column_double(hStmt, 1);
     167              26 :             rowId = sqlite3_column_int(hStmt, 2);
     168                 : 
     169              26 :             if (ipcb == 1) {
     170                 :                 /* add feature to the array */
     171              13 :                 poFeature = new VFKFeatureSQLite(this, rowId, iFID);
     172              26 :                 CPLAssert(NULL != poFeature && poFeature->GetFID() == iFID);
     173              13 :                 AddFeature(poFeature);
     174                 :                 
     175              13 :                 if (poLine) {
     176              12 :                     oOGRLine.setCoordinateDimension(2); /* force 2D */
     177              12 :         if (bValid) {
     178              12 :       if (!poLine->SetGeometry(&oOGRLine)) {
     179               0 :                             bValid = FALSE;
     180               0 :           nInvalid++;
     181                 :                         }
     182                 :         }
     183                 :         else {
     184               0 :       poLine->SetGeometry(NULL);
     185               0 :       nInvalid++;
     186                 :         }
     187                 :                     
     188                 :         /* update fid column */
     189              12 :         UpdateFID(poLine->GetFID(), rowIdFeat);        
     190                 : 
     191                 :         /* store also geometry in DB */
     192              12 :         CPLAssert(0 != rowIdFeat.size());
     193              12 :         if (bValid && poReader->IsSpatial() &&
     194                 :       SaveGeometryToDB(bValid ? &oOGRLine : NULL,
     195                 :            rowIdFeat[0]) != OGRERR_FAILURE)
     196              12 :       nGeometries++;
     197                 :         
     198              12 :         rowIdFeat.clear();
     199              12 :                     oOGRLine.empty(); /* restore line */
     200                 :                 }
     201              13 :     bValid = TRUE;
     202              13 :                 poLine = poFeature;
     203              13 :                 iFID++;
     204                 :             }
     205                 :             
     206              26 :             poPoint = (VFKFeatureSQLite *) poDataBlockPoints->GetFeature("ID", id);
     207              26 :       if (poPoint) {
     208              26 :     OGRPoint *pt = (OGRPoint *) poPoint->GetGeometry();
     209              26 :     if (pt) {
     210              26 :         oOGRLine.addPoint(pt);
     211                 :     }
     212                 :     else {
     213                 :         CPLDebug("OGR-VFK", 
     214               0 :            "Geometry (point ID = %lld) not valid", id);
     215               0 :         bValid = FALSE;
     216                 :     }
     217                 :       }
     218                 :       else {
     219                 :                 CPLDebug("OGR-VFK", 
     220                 :                          "Point ID = %lld not found (rowid = %d)",
     221               0 :                          id, rowId);
     222               0 :     bValid = FALSE;
     223                 :             }
     224                 :       
     225                 :       /* add vertex to the linestring */
     226              26 :       rowIdFeat.push_back(rowId);
     227                 :         }
     228                 : 
     229                 :         /* add last line */
     230               2 :         if (poLine) {
     231               1 :       oOGRLine.setCoordinateDimension(2); /* force 2D */
     232               1 :       if (bValid) {
     233               1 :     if (!poLine->SetGeometry(&oOGRLine))
     234               0 :         nInvalid++;
     235                 :       }
     236                 :       else {
     237               0 :     poLine->SetGeometry(NULL);
     238               0 :     nInvalid++;
     239                 :       }
     240                 :             
     241                 :       /* update fid column */
     242               1 :       UpdateFID(poLine->GetFID(), rowIdFeat);        
     243                 :       
     244                 :       /* store also geometry in DB */
     245               1 :       CPLAssert(0 != rowIdFeat.size());
     246               1 :       if (poReader->IsSpatial() &&
     247                 :     SaveGeometryToDB(bValid ? &oOGRLine : NULL,
     248                 :          rowIdFeat[0]) != OGRERR_FAILURE && bValid)
     249               1 :     nGeometries++;
     250                 :         }
     251               2 :   poLine = NULL;
     252               2 :   rowIdFeat.clear();
     253               2 :         oOGRLine.empty(); /* restore line */
     254                 : 
     255               2 :   if (poReader->IsSpatial())
     256               2 :       poReader->ExecuteSQL("COMMIT");
     257                 :     }
     258                 : 
     259                 :     /* update number of geometries in 'vfk_blocks' table */
     260               1 :     UpdateVfkBlocks(nGeometries);
     261                 :     
     262               1 :     return nInvalid;
     263                 : }
     264                 : 
     265                 : /*!
     266                 :   \brief Load geometry (linestring HP/DPM layer)
     267                 : 
     268                 :   \return number of invalid features
     269                 : */
     270               2 : int VFKDataBlockSQLite::LoadGeometryLineStringHP()
     271                 : {
     272                 :     int          nInvalid, nGeometries;
     273                 :     int          rowId;
     274                 :     long         iFID;
     275                 :     
     276               2 :     CPLString    osColumn, osSQL;
     277                 :     const char  *vrColumn[2];
     278                 :     GUIntBig     vrValue[2];
     279                 :     
     280                 :     sqlite3_stmt *hStmt;
     281                 :     
     282                 :     OGRGeometry        *poOgrGeometry;
     283                 :     VFKReaderSQLite    *poReader;
     284                 :     VFKDataBlockSQLite *poDataBlockLines;
     285                 :     VFKFeatureSQLite   *poFeature, *poLine;
     286                 :     
     287               2 :     nInvalid = nGeometries = 0;
     288               2 :     poReader  = (VFKReaderSQLite*) m_poReader;
     289                 :     
     290               2 :     poDataBlockLines = (VFKDataBlockSQLite *) m_poReader->GetDataBlock("SBP");
     291               2 :     if (NULL == poDataBlockLines) {
     292                 :         CPLError(CE_Failure, CPLE_FileIO, 
     293               0 :                  "Data block %s not found", m_pszName);
     294               0 :         return nInvalid;
     295                 :     }
     296                 :     
     297               2 :     poDataBlockLines->LoadGeometry();
     298                 : 
     299               2 :     if (LoadGeometryFromDB()) /* try to load geometry from DB */
     300               1 :   return 0;
     301                 :     
     302               1 :     osColumn.Printf("%s_ID", m_pszName);
     303               1 :     vrColumn[0] = osColumn.c_str();
     304               1 :     vrColumn[1] = "PORADOVE_CISLO_BODU";
     305               1 :     vrValue[1]  = 1; /* reduce to first segment */
     306                 :     
     307               1 :     osSQL.Printf("SELECT ID,%s,rowid FROM %s", FID_COLUMN, m_pszName);
     308                 :     /* TODO: handle points in DPM */
     309               1 :     if (EQUAL(m_pszName, "DPM"))
     310               0 :         osSQL += " WHERE SOURADNICE_X IS NULL";
     311               1 :     hStmt = poReader->PrepareStatement(osSQL.c_str());
     312                 :     
     313               1 :     if (poReader->IsSpatial())
     314               1 :   poReader->ExecuteSQL("BEGIN");
     315                 : 
     316              15 :     while(poReader->ExecuteSQL(hStmt) == OGRERR_NONE) {
     317                 :         /* read values */
     318              13 :         vrValue[0] = sqlite3_column_double(hStmt, 0);
     319              13 :         iFID       = sqlite3_column_double(hStmt, 1);
     320              13 :         rowId      = sqlite3_column_int(hStmt, 2);
     321                 : 
     322              13 :         poFeature = (VFKFeatureSQLite *) GetFeatureByIndex(rowId - 1);
     323              13 :         CPLAssert(NULL != poFeature && poFeature->GetFID() == iFID);
     324                 : 
     325              13 :         poLine = poDataBlockLines->GetFeature(vrColumn, vrValue, 2, TRUE);
     326              13 :   if (!poLine) {
     327               0 :       poOgrGeometry = NULL;
     328                 :   }
     329                 :   else {
     330              13 :       poOgrGeometry = poLine->GetGeometry();
     331                 :   }
     332              13 :   if (!poOgrGeometry || !poFeature->SetGeometry(poOgrGeometry)) {
     333               0 :       nInvalid++;
     334               0 :             continue;
     335                 :         }
     336                 :   
     337                 :   /* store also geometry in DB */
     338              13 :   if (poReader->IsSpatial() &&
     339                 :       SaveGeometryToDB(poOgrGeometry, rowId) != OGRERR_FAILURE &&
     340                 :       poOgrGeometry)
     341              13 :       nGeometries++;
     342                 :     }
     343                 :     
     344                 :     /* update number of geometries in 'vfk_blocks' table */
     345               1 :     UpdateVfkBlocks(nGeometries);
     346                 :     
     347               1 :     if (poReader->IsSpatial())
     348               1 :   poReader->ExecuteSQL("COMMIT");
     349                 : 
     350               1 :     return nInvalid;
     351                 : }
     352                 : 
     353                 : /*!
     354                 :   \brief Load geometry (polygon BUD/PAR layers)
     355                 : 
     356                 :   \return number of invalid features
     357                 : */
     358               2 : int VFKDataBlockSQLite::LoadGeometryPolygon()
     359                 : {
     360                 :     int  nInvalidNoLines, nInvalidNoRings, nGeometries, nBridges;
     361                 :     int  rowId, nCount, nCountMax;
     362                 :     size_t nLines;
     363                 :     long iFID;
     364                 :     bool bIsPar, bNewRing, bFound;
     365                 :         
     366               2 :     CPLString    osSQL;
     367                 :     const char  *vrColumn[2];
     368                 :     GUIntBig     vrValue[2];
     369                 :     GUIntBig     id, idOb;
     370                 :     
     371                 :     sqlite3_stmt *hStmt;
     372                 :     
     373                 :     VFKReaderSQLite    *poReader;
     374                 :     VFKDataBlockSQLite *poDataBlockLines1, *poDataBlockLines2;
     375                 :     VFKFeatureSQLite   *poFeature;
     376                 : 
     377               2 :     VFKFeatureSQLiteList  poLineList;
     378                 :     /* first is to be considered as exterior */
     379               2 :     PointListArray        poRingList;
     380                 :     
     381               2 :     std::vector<OGRLinearRing *> poLinearRingList;
     382               2 :     OGRPolygon     ogrPolygon;
     383                 :     OGRLinearRing *poOgrRing;
     384                 : 
     385               2 :     nInvalidNoLines = nInvalidNoRings = nGeometries = 0;
     386               2 :     poReader  = (VFKReaderSQLite*) m_poReader;
     387                 :     
     388               2 :     if (EQUAL (m_pszName, "PAR")) {
     389               2 :         poDataBlockLines1 = (VFKDataBlockSQLite *) m_poReader->GetDataBlock("HP");
     390               2 :         poDataBlockLines2 = poDataBlockLines1;
     391               2 :         bIsPar = TRUE;
     392                 :     }
     393                 :     else {
     394               0 :         poDataBlockLines1 = (VFKDataBlockSQLite *) m_poReader->GetDataBlock("OB");
     395               0 :         poDataBlockLines2 = (VFKDataBlockSQLite *) m_poReader->GetDataBlock("SBP");
     396               0 :         bIsPar = FALSE;
     397                 :     }
     398               2 :     if (NULL == poDataBlockLines1 || NULL == poDataBlockLines2) {
     399                 :         CPLError(CE_Failure, CPLE_FileIO, 
     400               0 :                  "Data block %s not found", m_pszName);
     401               0 :         return -1;
     402                 :     }
     403                 :     
     404               2 :     poDataBlockLines1->LoadGeometry();
     405               2 :     poDataBlockLines2->LoadGeometry();
     406                 :     
     407               2 :     if (LoadGeometryFromDB()) /* try to load geometry from DB */
     408               1 :   return 0;
     409                 :     
     410               1 :     if (bIsPar) {
     411               1 :         vrColumn[0] = "PAR_ID_1";
     412               1 :         vrColumn[1] = "PAR_ID_2";
     413                 :     }
     414                 :     else {
     415               0 :         vrColumn[0] = "OB_ID";
     416               0 :         vrColumn[1] = "PORADOVE_CISLO_BODU";
     417               0 :         vrValue[1]  = 1;
     418                 :     }
     419                 : 
     420               1 :     osSQL.Printf("SELECT ID,%s,rowid FROM %s", FID_COLUMN, m_pszName);
     421               1 :     hStmt = poReader->PrepareStatement(osSQL.c_str());
     422                 :     
     423               1 :     if (poReader->IsSpatial())
     424               1 :   poReader->ExecuteSQL("BEGIN");
     425                 : 
     426               3 :     while(poReader->ExecuteSQL(hStmt) == OGRERR_NONE) {
     427               1 :         nBridges = 0;
     428                 :         
     429                 :         /* read values */
     430               1 :         id        = sqlite3_column_double(hStmt, 0);
     431               1 :         iFID      = sqlite3_column_double(hStmt, 1);
     432               1 :         rowId     = sqlite3_column_int(hStmt, 2);
     433                 :         
     434               1 :         poFeature = (VFKFeatureSQLite *) GetFeatureByIndex(rowId - 1);
     435               1 :         CPLAssert(NULL != poFeature && poFeature->GetFID() == iFID);
     436                 : 
     437               1 :         if (bIsPar) {
     438               1 :             vrValue[0] = vrValue[1] = id;
     439               1 :             poLineList = poDataBlockLines1->GetFeatures(vrColumn, vrValue, 2);
     440                 :         }
     441                 :         else {
     442                 :             VFKFeatureSQLite *poLineSbp;
     443               0 :             std::vector<VFKFeatureSQLite *> poLineListOb;
     444                 :             sqlite3_stmt *hStmtOb;
     445                 :             
     446                 :             osSQL.Printf("SELECT ID FROM %s WHERE BUD_ID = " CPL_FRMT_GUIB,
     447               0 :                          poDataBlockLines1->GetName(), id);
     448               0 :             if (poReader->IsSpatial()) {
     449               0 :                 CPLString osColumn;
     450                 :                 
     451               0 :                 osColumn.Printf(" AND %s IS NULL", GEOM_COLUMN);
     452               0 :                 osSQL += osColumn;
     453                 :             }
     454               0 :             hStmtOb = poReader->PrepareStatement(osSQL.c_str());
     455                 :             
     456               0 :             while(poReader->ExecuteSQL(hStmtOb) == OGRERR_NONE) {
     457               0 :                 idOb = sqlite3_column_double(hStmtOb, 0); 
     458               0 :                 vrValue[0] = idOb;
     459               0 :                 poLineSbp = poDataBlockLines2->GetFeature(vrColumn, vrValue, 2);
     460               0 :                 if (poLineSbp)
     461               0 :                     poLineList.push_back(poLineSbp);
     462               0 :             }
     463                 :         }
     464               1 :         nLines = poLineList.size();
     465               1 :         if (nLines < 1) {
     466                 :             CPLDebug("OGR-VFK", 
     467                 :                      "%s: unable to collect rings for polygon fid = %ld (no lines)",
     468               0 :                      m_pszName, iFID);
     469               0 :             nInvalidNoLines++;
     470               0 :             continue;
     471                 :         }
     472                 :         
     473                 :         /* clear */
     474               1 :         ogrPolygon.empty();
     475               1 :         poRingList.clear();
     476                 :         
     477                 :         /* collect rings from lines */
     478               1 :         bFound = FALSE;
     479               1 :         nCount = 0;
     480               1 :         nCountMax = nLines * 2;
     481              15 :   while (poLineList.size() > 0 && nCount < nCountMax) {
     482              13 :             bNewRing = !bFound ? TRUE : FALSE;
     483              13 :             bFound = FALSE;
     484              13 :             int i = 1;
     485              51 :             for (VFKFeatureSQLiteList::iterator iHp = poLineList.begin(), eHp = poLineList.end();
     486                 :                  iHp != eHp; ++iHp, ++i) {
     487              51 :                 const OGRLineString *pLine = (OGRLineString *) (*iHp)->GetGeometry();
     488              51 :                 if (pLine && AppendLineToRing(&poRingList, pLine, bNewRing)) {
     489              13 :                     bFound = TRUE;
     490              13 :                     poLineList.erase(iHp);
     491              13 :                     break;
     492                 :                 }
     493                 :             }
     494              13 :             nCount++;
     495                 :         }
     496                 :         CPLDebug("OGR-VFK", "%s: fid = %ld nlines = %d -> nrings = %d", m_pszName,
     497               1 :                  iFID, (int)nLines, (int)poRingList.size());
     498                 : 
     499               1 :         if (poLineList.size() > 0) {
     500                 :             CPLDebug("OGR-VFK", 
     501                 :                      "%s: unable to collect rings for polygon fid = %ld",
     502               0 :                      m_pszName, iFID);
     503               0 :             nInvalidNoRings++;
     504               0 :             continue;
     505                 :         }
     506                 : 
     507                 :         /* build rings */
     508               1 :   poLinearRingList.clear();
     509               1 :   int i = 1;
     510               2 :         for (PointListArray::const_iterator iRing = poRingList.begin(), eRing = poRingList.end();
     511                 :              iRing != eRing; ++iRing) {
     512                 :       OGRPoint *poPoint;
     513               1 :             PointList *poList = *iRing;
     514                 :       
     515               1 :             poLinearRingList.push_back(new OGRLinearRing());
     516               1 :       poOgrRing = poLinearRingList.back();
     517               1 :       CPLAssert(NULL != poOgrRing);
     518                 :       
     519              15 :             for (PointList::iterator iPoint = poList->begin(), ePoint = poList->end();
     520                 :                  iPoint != ePoint; ++iPoint) {
     521              14 :     poPoint = &(*iPoint);
     522              14 :                 poOgrRing->addPoint(poPoint);
     523                 :             }
     524               1 :       i++;
     525                 :   }
     526                 : 
     527                 :   /* find exterior ring */
     528               1 :   if (poLinearRingList.size() > 1) {
     529                 :       double dArea, dMaxArea;
     530               0 :       std::vector<OGRLinearRing *>::iterator exteriorRing;
     531                 :       
     532               0 :       exteriorRing = poLinearRingList.begin();
     533               0 :       dMaxArea = -1.;
     534               0 :       for (std::vector<OGRLinearRing *>::iterator iRing = poLinearRingList.begin(),
     535               0 :          eRing = poLinearRingList.end(); iRing != eRing; ++iRing) {
     536               0 :     poOgrRing = *iRing;
     537               0 :     if (!IsRingClosed(poOgrRing))
     538               0 :         continue; /* skip unclosed rings */
     539                 :     
     540               0 :     dArea = poOgrRing->get_Area();
     541               0 :     if (dArea > dMaxArea) {
     542               0 :         dMaxArea = dArea;
     543               0 :         exteriorRing = iRing;
     544                 :     }
     545                 :       }
     546               0 :       if (exteriorRing != poLinearRingList.begin()) {
     547               0 :     std::swap(*poLinearRingList.begin(), *exteriorRing);
     548                 :       }
     549                 :   }
     550                 :   
     551                 :   /* build polygon from rings */
     552               3 :         for (std::vector<OGRLinearRing *>::iterator iRing = poLinearRingList.begin(),
     553               1 :      eRing = poLinearRingList.end(); iRing != eRing; ++iRing) {
     554               1 :       poOgrRing = *iRing;
     555                 :       
     556                 :       /* check if ring is closed */
     557               1 :       if (IsRingClosed(poOgrRing)) {
     558               1 :     ogrPolygon.addRing(poOgrRing);
     559                 :             }
     560                 :       else {
     561               0 :                 if (poOgrRing->getNumPoints() == 2) {
     562                 :                     CPLDebug("OGR-VFK", "%s: Polygon (fid = %ld) bridge removed",
     563               0 :                              m_pszName, iFID);
     564               0 :                     nBridges++;
     565                 :                 }
     566                 :                 else {
     567                 :                     CPLDebug("OGR-VFK",
     568                 :                              "%s: Polygon (fid = %ld) unclosed ring skipped",
     569               0 :                              m_pszName, iFID);
     570                 :                 }
     571                 :             }
     572               1 :       delete poOgrRing;
     573               1 :             *iRing = NULL;
     574                 :         }
     575                 : 
     576                 :         /* set polygon */
     577               1 :         ogrPolygon.setCoordinateDimension(2); /* force 2D */
     578               1 :         if (ogrPolygon.getNumInteriorRings() + nBridges != (int) poLinearRingList.size() - 1 ||
     579                 :             !poFeature->SetGeometry(&ogrPolygon)) {
     580               0 :             nInvalidNoRings++;
     581               0 :             continue;
     582                 :         }
     583                 :   
     584                 :         /* store also geometry in DB */
     585               1 :         if (poReader->IsSpatial() && 
     586                 :       SaveGeometryToDB(&ogrPolygon, rowId) != OGRERR_FAILURE)
     587               1 :             nGeometries++;
     588                 :     }
     589                 :     
     590                 :     /* free ring list */
     591               2 :     for (PointListArray::iterator iRing = poRingList.begin(), eRing = poRingList.end();
     592                 :          iRing != eRing; ++iRing) {
     593               1 :         delete (*iRing);
     594               1 :         *iRing = NULL;
     595                 :     }
     596                 :     
     597                 :     CPLDebug("OGR-VFK", "%s: nolines = %d norings = %d",
     598               1 :              m_pszName, nInvalidNoLines, nInvalidNoRings);
     599                 :     
     600                 :     /* update number of geometries in 'vfk_blocks' table */
     601               1 :     UpdateVfkBlocks(nGeometries);
     602                 : 
     603               1 :     if (poReader->IsSpatial())
     604               1 :   poReader->ExecuteSQL("COMMIT");
     605                 : 
     606               1 :     return nInvalidNoLines + nInvalidNoRings;
     607                 : }
     608                 : 
     609                 : /*!
     610                 :   \brief Get feature by FID
     611                 : 
     612                 :   Modifies next feature id.
     613                 :   
     614                 :   \param nFID feature id
     615                 : 
     616                 :   \return pointer to feature definition or NULL on failure (not found)
     617                 : */
     618               0 : IVFKFeature *VFKDataBlockSQLite::GetFeature(long nFID)
     619                 : {
     620                 :     int rowId;
     621               0 :     CPLString osSQL;
     622                 :     VFKReaderSQLite  *poReader;
     623                 :     
     624                 :     sqlite3_stmt *hStmt;
     625                 :          
     626               0 :     if (m_nFeatureCount < 0) {
     627               0 :         m_poReader->ReadDataRecords(this);
     628                 :     }
     629                 :     
     630               0 :     if (nFID < 1 || nFID > m_nFeatureCount)
     631               0 :         return NULL;
     632                 : 
     633               0 :     if (m_bGeometryPerBlock && !m_bGeometry) {
     634               0 :         LoadGeometry();
     635                 :     }
     636                 :     
     637               0 :     poReader = (VFKReaderSQLite*) m_poReader;
     638                 :     
     639                 :     osSQL.Printf("SELECT rowid FROM %s WHERE %s = %ld",
     640               0 :                  m_pszName, FID_COLUMN, nFID);
     641               0 :     if (EQUAL(m_pszName, "SBP")) {
     642               0 :         osSQL += " AND PORADOVE_CISLO_BODU = 1";
     643                 :     }
     644               0 :     hStmt = poReader->PrepareStatement(osSQL.c_str());
     645                 : 
     646               0 :     rowId = -1;
     647               0 :     if (poReader->ExecuteSQL(hStmt) == OGRERR_NONE) {
     648               0 :         rowId = sqlite3_column_int(hStmt, 0);
     649                 :     }
     650               0 :     sqlite3_finalize(hStmt);
     651                 :     
     652               0 :     return GetFeatureByIndex(rowId - 1);
     653                 : }
     654                 : 
     655                 : /*!
     656                 :   \brief Get first found feature based on it's property
     657                 :   
     658                 :   \param column property name
     659                 :   \param value property value
     660                 :   \param bGeom True to check also geometry != NULL
     661                 :     
     662                 :   \return pointer to feature definition or NULL on failure (not found)
     663                 : */
     664              26 : VFKFeatureSQLite *VFKDataBlockSQLite::GetFeature(const char *column, GUIntBig value,
     665                 :                                                  bool bGeom)
     666                 : {
     667                 :     int idx;
     668              26 :     CPLString osSQL;
     669                 :     VFKReaderSQLite  *poReader;
     670                 : 
     671                 :     sqlite3_stmt *hStmt;
     672                 :     
     673              26 :     poReader = (VFKReaderSQLite*) m_poReader;
     674                 :     
     675                 :     osSQL.Printf("SELECT %s from %s WHERE %s = " CPL_FRMT_GUIB,
     676              26 :                  FID_COLUMN, m_pszName, column, value);
     677              26 :     if (bGeom) {
     678               0 :         CPLString osColumn;
     679                 :         
     680               0 :         osColumn.Printf(" AND %s IS NOT NULL", GEOM_COLUMN);
     681               0 :         osSQL += osColumn;
     682                 :     }
     683                 :     
     684              26 :     hStmt = poReader->PrepareStatement(osSQL.c_str());
     685              26 :     if (poReader->ExecuteSQL(hStmt) != OGRERR_NONE)
     686               0 :         return NULL;
     687                 :     
     688              26 :     idx = sqlite3_column_int(hStmt, 0) - 1;
     689              26 :     if (idx < 0 || idx >= m_nFeatureCount) // ? assert
     690               0 :         return NULL;
     691                 :     
     692              26 :     sqlite3_finalize(hStmt);
     693                 : 
     694              26 :     return (VFKFeatureSQLite *) GetFeatureByIndex(idx);
     695                 : }
     696                 : 
     697                 : /*!
     698                 :   \brief Get first found feature based on it's properties (AND)
     699                 :   
     700                 :   \param column array of property names
     701                 :   \param value array of property values
     702                 :   \param num number of array items
     703                 :   \param bGeom True to check also geometry != NULL
     704                 : 
     705                 :   \return pointer to feature definition or NULL on failure (not found)
     706                 : */
     707              13 : VFKFeatureSQLite *VFKDataBlockSQLite::GetFeature(const char **column, GUIntBig *value, int num,
     708                 :                                                  bool bGeom)
     709                 : {
     710                 :     int idx;
     711              13 :     CPLString osSQL, osItem;
     712                 :     VFKReaderSQLite  *poReader;
     713                 : 
     714                 :     sqlite3_stmt *hStmt;
     715                 :     
     716              13 :     poReader = (VFKReaderSQLite*) m_poReader;
     717                 :     
     718              13 :     osSQL.Printf("SELECT %s FROM %s WHERE ", FID_COLUMN, m_pszName);
     719              39 :     for (int i = 0; i < num; i++) {
     720              26 :         if (i > 0)
     721              13 :             osItem.Printf(" AND %s = " CPL_FRMT_GUIB, column[i], value[i]);
     722                 :         else
     723              13 :             osItem.Printf("%s = " CPL_FRMT_GUIB, column[i], value[i]);
     724              26 :         osSQL += osItem;
     725                 :     }
     726              13 :     if (bGeom) {
     727              13 :         osItem.Printf(" AND %s IS NOT NULL", GEOM_COLUMN);
     728              13 :         osSQL += osItem;
     729                 :     }
     730                 : 
     731              13 :     hStmt = poReader->PrepareStatement(osSQL.c_str());
     732              13 :     if (poReader->ExecuteSQL(hStmt) != OGRERR_NONE)
     733               0 :         return NULL;
     734                 :     
     735              13 :     idx = sqlite3_column_int(hStmt, 0) - 1; /* rowid starts at 1 */
     736                 :     
     737              13 :     if (idx < 0 || idx >= m_nFeatureCount) // ? assert
     738               0 :         return NULL;
     739                 : 
     740              13 :     sqlite3_finalize(hStmt);
     741                 :     
     742              13 :     return (VFKFeatureSQLite *) GetFeatureByIndex(idx);
     743                 : }
     744                 : 
     745                 : /*!
     746                 :   \brief Get features based on properties
     747                 :   
     748                 :   \param column array of property names
     749                 :   \param value array of property values
     750                 :   \param num number of array items
     751                 :   
     752                 :   \return list of features
     753                 : */
     754               1 : VFKFeatureSQLiteList VFKDataBlockSQLite::GetFeatures(const char **column, GUIntBig *value, int num)
     755                 : {
     756                 :     int iRowId;
     757               1 :     CPLString osSQL, osItem;
     758                 : 
     759                 :     VFKReaderSQLite     *poReader;
     760               1 :     VFKFeatureSQLiteList fList;
     761                 :     
     762                 :     sqlite3_stmt *hStmt;
     763                 :     
     764               1 :     poReader = (VFKReaderSQLite*) m_poReader;
     765                 :     
     766               1 :     osSQL.Printf("SELECT rowid from %s WHERE ", m_pszName);
     767               3 :     for (int i = 0; i < num; i++) {
     768               2 :         if (i > 0)
     769               1 :             osItem.Printf(" OR %s = " CPL_FRMT_GUIB, column[i], value[i]);
     770                 :         else
     771               1 :             osItem.Printf("%s = " CPL_FRMT_GUIB, column[i], value[i]);
     772               2 :         osSQL += osItem;
     773                 :     }
     774               1 :     osSQL += " ORDER BY ";
     775               1 :     osSQL += FID_COLUMN;
     776                 :     
     777               1 :     hStmt = poReader->PrepareStatement(osSQL.c_str());
     778              15 :     while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE) {
     779              13 :         iRowId = sqlite3_column_int(hStmt, 0);
     780              13 :         fList.push_back((VFKFeatureSQLite *)GetFeatureByIndex(iRowId - 1));
     781                 :     }
     782                 :     
     783               1 :     return fList;
     784                 : }
     785                 : 
     786                 : /*!
     787                 :   \brief Save geometry to DB (as WKB)
     788                 : 
     789                 :   \param poGeom pointer to OGRGeometry to be saved
     790                 :   \param iRowId row id to update
     791                 : 
     792                 :   \return OGRERR_NONE on success otherwise OGRERR_FAILURE
     793                 : */
     794              40 : OGRErr VFKDataBlockSQLite::SaveGeometryToDB(const OGRGeometry *poGeom, int iRowId)
     795                 : {
     796                 :     int        rc, nWKBLen;
     797                 :     GByte     *pabyWKB;
     798              40 :     CPLString  osSQL;
     799                 : 
     800                 :     sqlite3_stmt *hStmt;
     801                 : 
     802                 :     VFKReaderSQLite  *poReader;
     803                 : 
     804              40 :     poReader  = (VFKReaderSQLite*) m_poReader;
     805                 : 
     806              40 :     if (poGeom) {
     807              40 :   nWKBLen = poGeom->WkbSize();
     808              40 :   pabyWKB = (GByte *) CPLMalloc(nWKBLen + 1);
     809              40 :   poGeom->exportToWkb(wkbNDR, pabyWKB);
     810                 :         
     811                 :   osSQL.Printf("UPDATE %s SET %s = ? WHERE rowid = %d",
     812              40 :          m_pszName, GEOM_COLUMN, iRowId);
     813              40 :   hStmt = poReader->PrepareStatement(osSQL.c_str());
     814                 :   
     815              40 :   rc = sqlite3_bind_blob(hStmt, 1, pabyWKB, nWKBLen, CPLFree);
     816              40 :   if (rc != SQLITE_OK) {
     817               0 :       sqlite3_finalize(hStmt);
     818                 :       CPLError(CE_Failure, CPLE_AppDefined, 
     819               0 :          "Storing geometry in DB failed");
     820               0 :       return OGRERR_FAILURE;
     821                 :   }
     822                 :     }
     823                 :     else { /* invalid */
     824                 :   osSQL.Printf("UPDATE %s SET %s = NULL WHERE rowid = %d",
     825               0 :          m_pszName, GEOM_COLUMN, iRowId);
     826               0 :   hStmt = poReader->PrepareStatement(osSQL.c_str());
     827                 :     }
     828                 : 
     829              40 :     return poReader->ExecuteSQL(hStmt); /* calls sqlite3_finalize() */
     830                 : }
     831                 : 
     832                 : /*!
     833                 :   \brief Load geometry from DB
     834                 : 
     835                 :   \return TRUE geometry successfully loaded otherwise FALSE
     836                 : */
     837               8 : bool VFKDataBlockSQLite::LoadGeometryFromDB()
     838                 : {
     839                 :     int nInvalid, nGeometries, nGeometriesCount, nBytes, rowId;
     840                 :     long iFID;
     841                 :     bool bAddFeature, bSkipInvalid;
     842                 :     
     843               8 :     CPLString osSQL;
     844                 :     
     845                 :     OGRGeometry      *poGeometry;
     846                 :     
     847                 :     VFKFeatureSQLite *poFeature;
     848                 :     VFKReaderSQLite  *poReader;
     849                 :     
     850                 :     sqlite3_stmt *hStmt;
     851                 :     
     852               8 :     poReader = (VFKReaderSQLite*) m_poReader;
     853                 : 
     854               8 :     if (!poReader->IsSpatial())   /* check if DB is spatial */
     855               0 :   return FALSE;
     856                 : 
     857                 :     osSQL.Printf("SELECT num_geometries FROM 'vfk_blocks' WHERE table_name = '%s'",
     858               8 :      m_pszName);
     859               8 :     hStmt = poReader->PrepareStatement(osSQL.c_str());
     860               8 :     if (poReader->ExecuteSQL(hStmt) != OGRERR_NONE)
     861               0 :         return FALSE;
     862               8 :     nGeometries = sqlite3_column_int(hStmt, 0);
     863               8 :     sqlite3_finalize(hStmt);
     864                 :     
     865               8 :     if (nGeometries < 1)
     866               4 :   return FALSE;
     867                 :     
     868               4 :     bAddFeature = EQUAL(m_pszName, "SBP");
     869               4 :     bSkipInvalid = EQUAL(m_pszName, "OB") || EQUAL(m_pszName, "OP") || EQUAL(m_pszName, "OBBP");
     870                 :     
     871                 :     /* load geometry from DB */
     872               4 :     nInvalid = nGeometriesCount = 0;
     873                 :     osSQL.Printf("SELECT %s,rowid,%s FROM %s ",
     874               4 :      GEOM_COLUMN, FID_COLUMN, m_pszName);
     875               4 :     if (EQUAL(m_pszName, "SBP"))
     876               1 :   osSQL += "WHERE PORADOVE_CISLO_BODU = 1 ";
     877               4 :     osSQL += "ORDER BY ";
     878               4 :     osSQL += FID_COLUMN;
     879               4 :     hStmt = poReader->PrepareStatement(osSQL.c_str());
     880                 : 
     881              48 :     while(poReader->ExecuteSQL(hStmt) == OGRERR_NONE) {
     882              40 :         rowId = sqlite3_column_int(hStmt, 1);
     883              40 :         iFID = sqlite3_column_double(hStmt, 2);
     884                 : 
     885              40 :         if (bAddFeature) {
     886                 :             /* add feature to the array */
     887              13 :             poFeature = new VFKFeatureSQLite(this, rowId, iFID);
     888              13 :             AddFeature(poFeature);
     889                 :         }
     890                 :         else {
     891              27 :             poFeature = (VFKFeatureSQLite *) GetFeatureByIndex(rowId - 1);
     892                 :         }
     893              40 :         CPLAssert(NULL != poFeature && poFeature->GetFID() == iFID);
     894                 :         
     895                 :         // read geometry from DB
     896              40 :   nBytes = sqlite3_column_bytes(hStmt, 0);
     897              40 :   if (nBytes > 0 &&
     898                 :       OGRGeometryFactory::createFromWkb((GByte*) sqlite3_column_blob(hStmt, 0),
     899                 :                 NULL, &poGeometry, nBytes) == OGRERR_NONE) {
     900              40 :       nGeometriesCount++;
     901              40 :       if (!poFeature->SetGeometry(poGeometry)) {
     902               0 :     nInvalid++;
     903                 :       }
     904                 :   }
     905                 :   else {
     906               0 :       nInvalid++;
     907                 :   }
     908                 :     }
     909                 : 
     910                 :     CPLDebug("OGR-VFK", "%s: %d geometries loaded from DB",
     911               4 :        m_pszName, nGeometriesCount);
     912                 : 
     913               4 :     if (nGeometriesCount != nGeometries) {
     914                 :   CPLError(CE_Warning, CPLE_AppDefined, 
     915                 :                  "%s: %d geometries loaded (should be %d)",
     916               0 :      m_pszName, nGeometriesCount, nGeometries);
     917                 :     }
     918                 : 
     919               4 :     if (nInvalid > 0 && !bSkipInvalid) {
     920                 :   CPLError(CE_Warning, CPLE_AppDefined, 
     921                 :                  "%s: %d invalid features found",
     922               0 :      m_pszName, nInvalid);
     923                 :     }
     924                 : 
     925               4 :     return TRUE;
     926                 : }
     927                 : 
     928                 : /*!
     929                 :   \brief Update vfk_blocks table
     930                 : 
     931                 :   \param nGeometries number of geometries to update
     932                 : */
     933               4 : void VFKDataBlockSQLite::UpdateVfkBlocks(int nGeometries) {
     934               4 :     CPLString osSQL;
     935                 :     
     936                 :     VFKReaderSQLite  *poReader;
     937                 :     
     938               4 :     poReader = (VFKReaderSQLite*) m_poReader;
     939                 : 
     940               4 :     if (nGeometries > 0) {
     941                 :         CPLDebug("OGR-VFK", 
     942               4 :                  "%d geometries in '%s' saved", nGeometries, m_pszName);
     943                 :         
     944                 :   /* update number of geometries in 'vfk_blocks' table */
     945                 :   osSQL.Printf("UPDATE vfk_blocks SET num_geometries = %d WHERE table_name = '%s'",
     946               4 :          nGeometries, m_pszName);
     947               4 :   poReader->ExecuteSQL(osSQL.c_str());
     948               4 :     }
     949               4 : }
     950                 : 
     951                 : /*!
     952                 :   \brief Update feature id (see SBP)
     953                 : 
     954                 :   \param iFID feature id to set up
     955                 :   \param rowId list of rows to update
     956                 : */
     957              13 : void VFKDataBlockSQLite::UpdateFID(long int iFID, std::vector<int> rowId)
     958                 : {
     959              13 :     CPLString osSQL, osValue;
     960                 :     VFKReaderSQLite  *poReader;
     961                 :     
     962              13 :     poReader = (VFKReaderSQLite*) m_poReader;
     963                 :     
     964                 :     /* update number of geometries in 'vfk_blocks' table */
     965                 :     osSQL.Printf("UPDATE %s SET %s = %ld WHERE rowid IN (",
     966              13 :                  m_pszName, FID_COLUMN, iFID);
     967              39 :     for (size_t i = 0; i < rowId.size(); i++) {
     968              26 :   if (i > 0)
     969              13 :       osValue.Printf(",%d", rowId[i]);
     970                 :   else
     971              13 :       osValue.Printf("%d", rowId[i]);
     972              26 :   osSQL += osValue;
     973                 :     }
     974              13 :     osSQL += ")";
     975                 :     
     976              13 :     poReader->ExecuteSQL(osSQL.c_str());
     977              13 : }
     978                 : 
     979                 : /*!
     980                 :   \brief Check is ring is closed
     981                 : 
     982                 :   \param poRing pointer to OGRLinearRing to check
     983                 : 
     984                 :   \return TRUE if closed otherwise FALSE
     985                 : */
     986               1 : bool VFKDataBlockSQLite::IsRingClosed(const OGRLinearRing *poRing)
     987                 : {
     988                 :     int nPoints;
     989                 : 
     990               1 :     nPoints = poRing->getNumPoints();
     991               1 :     if (nPoints < 3)
     992               0 :   return FALSE;
     993                 : 
     994               1 :     if (poRing->getX(0) == poRing->getX(nPoints-1) &&
     995                 :   poRing->getY(0) == poRing->getY(nPoints-1))
     996               1 :   return TRUE;
     997                 : 
     998               0 :     return FALSE;
     999                 : }
    1000                 : 
    1001                 : /*!
    1002                 :   \brief Get primary key
    1003                 : 
    1004                 :   \return property name or NULL
    1005                 : */
    1006               4 : const char *VFKDataBlockSQLite::GetKey() const
    1007                 : {
    1008                 :     const char *pszKey;
    1009                 :     const VFKPropertyDefn *poPropDefn;
    1010                 : 
    1011               4 :     if (GetPropertyCount() > 1) {
    1012               4 :         poPropDefn = GetProperty(0);
    1013               4 :         pszKey = poPropDefn->GetName();
    1014               4 :         if (EQUAL(pszKey, "ID"))
    1015               4 :             return pszKey;
    1016                 :     }
    1017                 : 
    1018               0 :     return NULL;
    1019                 : }

Generated by: LCOV version 1.7