LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/vfk - vfkdatablocksqlite.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 460 602 76.4 %
Date: 2024-05-04 12:52:34 Functions: 19 20 95.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  VFK Reader - Data block definition (SQLite)
       4             :  * Purpose:  Implements VFKDataBlockSQLite
       5             :  * Author:   Martin Landa, landa.martin gmail.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2012-2014, Martin Landa <landa.martin gmail.com>
       9             :  * Copyright (c) 2012-2014, Even Rouault <even dot rouault at spatialys.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             : #include <limits>
      34             : #include <map>
      35             : #include <utility>
      36             : 
      37             : #include "vfkreader.h"
      38             : #include "vfkreaderp.h"
      39             : 
      40             : #include "cpl_conv.h"
      41             : #include "cpl_error.h"
      42             : 
      43             : /*!
      44             :   \brief VFKDataBlockSQLite constructor
      45             : */
      46         976 : VFKDataBlockSQLite::VFKDataBlockSQLite(const char *pszName,
      47         976 :                                        const IVFKReader *poReader)
      48         976 :     : IVFKDataBlock(pszName, poReader), m_hStmt(nullptr)
      49             : {
      50         976 : }
      51             : 
      52             : /*!
      53             :   \brief Load geometry (point layers)
      54             : 
      55             :   \return number of invalid features
      56             : */
      57          90 : int VFKDataBlockSQLite::LoadGeometryPoint()
      58             : {
      59          90 :     if (LoadGeometryFromDB()) /* try to load geometry from DB */
      60           1 :         return 0;
      61             : 
      62         252 :     const bool bSkipInvalid = EQUAL(m_pszName, "OB") ||
      63         148 :                               EQUAL(m_pszName, "OP") ||
      64          59 :                               EQUAL(m_pszName, "OBBP");
      65             : 
      66          89 :     CPLString osSQL;
      67             :     osSQL.Printf("SELECT SOURADNICE_Y,SOURADNICE_X,%s,rowid FROM %s",
      68          89 :                  FID_COLUMN, m_pszName);
      69             : 
      70          89 :     VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
      71          89 :     sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
      72             : 
      73          89 :     if (poReader->IsSpatial())
      74          89 :         poReader->ExecuteSQL("BEGIN");
      75             : 
      76          89 :     int nGeometries = 0;
      77          89 :     int nInvalid = 0;
      78         271 :     while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
      79             :     {
      80             :         /* read values */
      81             :         const double x =
      82         182 :             -1.0 * sqlite3_column_double(
      83         182 :                        hStmt, 0); /* S-JTSK coordinate system expected */
      84         182 :         const double y = -1.0 * sqlite3_column_double(hStmt, 1);
      85         182 :         const GIntBig iFID = sqlite3_column_int64(hStmt, 2);
      86         182 :         const int rowId = sqlite3_column_int(hStmt, 3);
      87             : 
      88             :         VFKFeatureSQLite *poFeature =
      89         182 :             dynamic_cast<VFKFeatureSQLite *>(GetFeatureByIndex(rowId - 1));
      90         182 :         if (poFeature == nullptr || poFeature->GetFID() != iFID)
      91             :         {
      92           0 :             continue;
      93             :         }
      94             : 
      95             :         /* create geometry */
      96         182 :         OGRPoint pt(x, y);
      97         182 :         if (!poFeature->SetGeometry(&pt))
      98             :         {
      99           0 :             nInvalid++;
     100           0 :             continue;
     101             :         }
     102             : 
     103             :         /* store also geometry in DB */
     104         364 :         if (poReader->IsSpatial() &&
     105         182 :             SaveGeometryToDB(&pt, rowId) != OGRERR_FAILURE)
     106         182 :             nGeometries++;
     107             :     }
     108             : 
     109             :     /* update number of geometries in VFK_DB_TABLE table */
     110          89 :     UpdateVfkBlocks(nGeometries);
     111             : 
     112          89 :     if (poReader->IsSpatial())
     113          89 :         poReader->ExecuteSQL("COMMIT");
     114             : 
     115          89 :     return bSkipInvalid ? 0 : nInvalid;
     116             : }
     117             : 
     118             : /*!
     119             :   \brief Set geometry for linestrings
     120             : 
     121             :   \param poLine VFK feature
     122             :   \param oOGRLine line geometry
     123             :   \param[in,out] bValid true when feature's geometry is valid
     124             :   \param ftype geometry VFK type
     125             :   \param[in,out] rowIdFeat list of row ids which forms linestring
     126             :   \param[in,out] nGeometries number of features with valid geometry
     127             : */
     128         196 : bool VFKDataBlockSQLite::SetGeometryLineString(VFKFeatureSQLite *poLine,
     129             :                                                OGRLineString *oOGRLine,
     130             :                                                bool &bValid, const char *ftype,
     131             :                                                std::vector<int> &rowIdFeat,
     132             :                                                int &nGeometries)
     133             : {
     134         196 :     VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
     135             : 
     136         196 :     oOGRLine->setCoordinateDimension(2); /* force 2D */
     137             : 
     138             :     /* check also VFK validity */
     139         196 :     if (bValid)
     140             :     {
     141             :         /* Feature types
     142             : 
     143             :            - '3'    - line       (2 points)
     144             :            - '4'    - linestring (at least 2 points)
     145             :            - '11'   - curve      (at least 2 points)
     146             :            - '15'   - circle     (3 points)
     147             :            - '15 r' - circle     (center point & radius)
     148             :            - '16'   - arc        (3 points)
     149             :         */
     150             : 
     151         196 :         const int npoints = oOGRLine->getNumPoints();
     152         196 :         if (EQUAL(ftype, "3") && npoints > 2)
     153             :         {
     154             :             /* be less pedantic, just inform user about data
     155             :              * inconsistency
     156             : 
     157             :                bValid = false;
     158             :             */
     159           0 :             CPLDebug("OGR-VFK",
     160             :                      "Line (fid=" CPL_FRMT_GIB
     161             :                      ") defined by more than two vertices",
     162             :                      poLine->GetFID());
     163             :         }
     164         196 :         else if (EQUAL(ftype, "11") && npoints < 2)
     165             :         {
     166           0 :             bValid = false;
     167           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     168             :                      "Curve (fid=" CPL_FRMT_GIB
     169             :                      ") defined by less than two vertices",
     170             :                      poLine->GetFID());
     171             :         }
     172         196 :         else if (EQUAL(ftype, "15") && npoints != 3)
     173             :         {
     174           0 :             bValid = false;
     175           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     176             :                      "Circle (fid=" CPL_FRMT_GIB
     177             :                      ") defined by invalid number of vertices (%d)",
     178           0 :                      poLine->GetFID(), oOGRLine->getNumPoints());
     179             :         }
     180         196 :         else if (strlen(ftype) > 2 && STARTS_WITH_CI(ftype, "15") &&
     181             :                  npoints != 1)
     182             :         {
     183           0 :             bValid = false;
     184           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     185             :                      "Circle (fid=" CPL_FRMT_GIB
     186             :                      ") defined by invalid number of vertices (%d)",
     187           0 :                      poLine->GetFID(), oOGRLine->getNumPoints());
     188             :         }
     189         196 :         else if (EQUAL(ftype, "16") && npoints != 3)
     190             :         {
     191           0 :             bValid = false;
     192           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     193             :                      "Arc (fid=" CPL_FRMT_GIB
     194             :                      ") defined by invalid number of vertices (%d)",
     195           0 :                      poLine->GetFID(), oOGRLine->getNumPoints());
     196             :         }
     197             :     }
     198             : 
     199             :     /* set geometry (NULL for invalid features) */
     200         196 :     if (bValid)
     201             :     {
     202         196 :         if (!poLine->SetGeometry(oOGRLine, ftype))
     203             :         {
     204           0 :             bValid = false;
     205             :         }
     206             :     }
     207             :     else
     208             :     {
     209           0 :         poLine->SetGeometry(nullptr);
     210             :     }
     211             : 
     212             :     /* update fid column */
     213         196 :     UpdateFID(poLine->GetFID(), rowIdFeat);
     214             : 
     215             :     /* store also geometry in DB */
     216         196 :     CPLAssert(!rowIdFeat.empty());
     217         392 :     if (bValid && poReader->IsSpatial() &&
     218         196 :         SaveGeometryToDB(poLine->GetGeometry(), rowIdFeat[0]) != OGRERR_FAILURE)
     219             :     {
     220         196 :         nGeometries++;
     221             :     }
     222             : 
     223         196 :     rowIdFeat.clear();
     224         196 :     oOGRLine->empty(); /* restore line */
     225             : 
     226         196 :     return bValid;
     227             : }
     228             : 
     229             : /*!
     230             :   \brief Load geometry (linestring SBP layer)
     231             : 
     232             :   \return number of invalid features
     233             : */
     234          15 : int VFKDataBlockSQLite::LoadGeometryLineStringSBP()
     235             : {
     236          15 :     int nInvalid = 0;
     237             : 
     238             :     VFKDataBlockSQLite *poDataBlockPoints =
     239          15 :         (VFKDataBlockSQLite *)m_poReader->GetDataBlock("SOBR");
     240          15 :     if (nullptr == poDataBlockPoints)
     241             :     {
     242           0 :         CPLError(CE_Failure, CPLE_FileIO, "Data block %s not found.\n",
     243             :                  m_pszName);
     244           0 :         return nInvalid;
     245             :     }
     246             : 
     247          15 :     int nGeometries = 0;
     248          15 :     VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
     249             : 
     250          15 :     poDataBlockPoints->LoadGeometry();
     251             : 
     252          15 :     if (LoadGeometryFromDB()) /* try to load geometry from DB */
     253           1 :         return 0;
     254             : 
     255          14 :     CPLString osSQL;
     256          14 :     osSQL.Printf("UPDATE %s SET %s = -1", m_pszName, FID_COLUMN);
     257          14 :     poReader->ExecuteSQL(osSQL.c_str());
     258          14 :     bool bValid = true;
     259          14 :     int iIdx = 0;
     260             : 
     261          14 :     VFKFeatureSQLite *poLine = nullptr;
     262             : 
     263          42 :     for (int i = 0; i < 2; i++)
     264             :     {
     265             :         /* first collect linestrings related to HP, OB, DPM and ZVB
     266             :            then collect rest of linestrings */
     267          28 :         if (i == 0)
     268             :             osSQL.Printf(
     269             :                 "SELECT BP_ID,PORADOVE_CISLO_BODU,PARAMETRY_SPOJENI,_rowid_ "
     270             :                 "FROM '%s' WHERE "
     271             :                 "HP_ID IS NOT NULL OR OB_ID IS NOT NULL OR DPM_ID IS NOT NULL "
     272             :                 "OR ZVB_ID IS NOT NULL "
     273             :                 "ORDER BY HP_ID,OB_ID,DPM_ID,ZVB_ID,PORADOVE_CISLO_BODU",
     274          14 :                 m_pszName);
     275             :         else
     276             :             osSQL.Printf(
     277             :                 "SELECT BP_ID,PORADOVE_CISLO_BODU,PARAMETRY_SPOJENI,_rowid_ "
     278             :                 "FROM '%s' WHERE "
     279             :                 "OB_ID IS NULL AND HP_ID IS NULL AND DPM_ID IS NULL AND ZVB_ID "
     280             :                 "IS NULL "
     281             :                 "ORDER BY ID,PORADOVE_CISLO_BODU",
     282          14 :                 m_pszName);
     283             : 
     284          28 :         sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
     285             : 
     286          28 :         if (poReader->IsSpatial())
     287          28 :             poReader->ExecuteSQL("BEGIN");
     288             : 
     289          56 :         std::vector<int> rowIdFeat;
     290          56 :         CPLString osFType;
     291          56 :         OGRLineString oOGRLine;
     292             : 
     293         434 :         while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
     294             :         {
     295             :             // read values
     296         406 :             const GUIntBig id = sqlite3_column_int64(hStmt, 0);
     297         406 :             const GUIntBig ipcb = sqlite3_column_int64(hStmt, 1);
     298             :             const char *pszFType =
     299         406 :                 reinterpret_cast<const char *>(sqlite3_column_text(hStmt, 2));
     300         406 :             int rowId = sqlite3_column_int(hStmt, 3);
     301             : 
     302         406 :             if (ipcb == 1)
     303             :             {
     304             :                 VFKFeatureSQLite *poFeature =
     305         196 :                     (VFKFeatureSQLite *)GetFeatureByIndex(iIdx);
     306         196 :                 if (poFeature == nullptr)
     307             :                 {
     308           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     309             :                              "Cannot retrieve feature %d", iIdx);
     310           0 :                     sqlite3_finalize(hStmt);
     311           0 :                     break;
     312             :                 }
     313         196 :                 poFeature->SetRowId(rowId);
     314             : 
     315             :                 /* set geometry & reset */
     316         196 :                 if (poLine && !SetGeometryLineString(poLine, &oOGRLine, bValid,
     317             :                                                      osFType.c_str(), rowIdFeat,
     318             :                                                      nGeometries))
     319             :                 {
     320           0 :                     nInvalid++;
     321             :                 }
     322             : 
     323         196 :                 bValid = true;
     324         196 :                 poLine = poFeature;
     325         196 :                 osFType = pszFType ? pszFType : "";
     326         196 :                 iIdx++;
     327             :             }
     328             : 
     329             :             VFKFeatureSQLite *poPoint =
     330         406 :                 (VFKFeatureSQLite *)poDataBlockPoints->GetFeature("ID", id);
     331         406 :             if (poPoint)
     332             :             {
     333         406 :                 const OGRGeometry *pt = poPoint->GetGeometry();
     334         406 :                 if (pt)
     335             :                 {
     336         406 :                     oOGRLine.addPoint(pt->toPoint());
     337             :                 }
     338             :                 else
     339             :                 {
     340           0 :                     CPLDebug("OGR-VFK",
     341             :                              "Geometry (point ID = " CPL_FRMT_GUIB
     342             :                              ") not valid",
     343             :                              id);
     344           0 :                     bValid = false;
     345             :                 }
     346             :             }
     347             :             else
     348             :             {
     349           0 :                 CPLDebug("OGR-VFK",
     350             :                          "Point ID = " CPL_FRMT_GUIB " not found (rowid = %d)",
     351             :                          id, rowId);
     352           0 :                 bValid = false;
     353             :             }
     354             : 
     355             :             /* add vertex to the linestring */
     356         406 :             rowIdFeat.push_back(rowId);
     357             :         }
     358             : 
     359             :         /* add last line */
     360          42 :         if (poLine &&
     361          14 :             !SetGeometryLineString(poLine, &oOGRLine, bValid, osFType.c_str(),
     362             :                                    rowIdFeat, nGeometries))
     363             :         {
     364           0 :             nInvalid++;
     365             :         }
     366          28 :         poLine = nullptr;
     367             : 
     368          28 :         if (poReader->IsSpatial())
     369          28 :             poReader->ExecuteSQL("COMMIT");
     370             :     }
     371             : 
     372             :     /* update number of geometries in VFK_DB_TABLE table */
     373          14 :     UpdateVfkBlocks(nGeometries);
     374             : 
     375          14 :     return nInvalid;
     376             : }
     377             : 
     378             : /*!
     379             :   \brief Load geometry (linestring HP/DPM/ZVB layer)
     380             : 
     381             :   \return number of invalid features
     382             : */
     383          45 : int VFKDataBlockSQLite::LoadGeometryLineStringHP()
     384             : {
     385          45 :     int nInvalid = 0;
     386          45 :     VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
     387             : 
     388             :     VFKDataBlockSQLite *poDataBlockLines =
     389          45 :         (VFKDataBlockSQLite *)m_poReader->GetDataBlock("SBP");
     390          45 :     if (nullptr == poDataBlockLines)
     391             :     {
     392           0 :         CPLError(CE_Failure, CPLE_FileIO, "Data block %s not found.",
     393             :                  m_pszName);
     394           0 :         return nInvalid;
     395             :     }
     396             : 
     397          45 :     poDataBlockLines->LoadGeometry();
     398             : 
     399          45 :     if (LoadGeometryFromDB()) /* try to load geometry from DB */
     400           1 :         return 0;
     401             : 
     402          88 :     CPLString osColumn;
     403          44 :     osColumn.Printf("%s_ID", m_pszName);
     404          44 :     const char *vrColumn[2] = {osColumn.c_str(), "PORADOVE_CISLO_BODU"};
     405             : 
     406          44 :     GUIntBig vrValue[2] = {0, 1};  // Reduce to first segment.
     407             : 
     408          44 :     CPLString osSQL;
     409          44 :     osSQL.Printf("SELECT ID,%s,rowid FROM %s", FID_COLUMN, m_pszName);
     410             :     /* TODO: handle points in DPM */
     411          44 :     if (EQUAL(m_pszName, "DPM"))
     412          15 :         osSQL += " WHERE SOURADNICE_X IS NULL";
     413          44 :     sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
     414             : 
     415          44 :     if (poReader->IsSpatial())
     416          44 :         poReader->ExecuteSQL("BEGIN");
     417             : 
     418          44 :     int nGeometries = 0;
     419             : 
     420         226 :     while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
     421             :     {
     422             :         /* read values */
     423         182 :         vrValue[0] = sqlite3_column_int64(hStmt, 0);
     424         182 :         const GIntBig iFID = sqlite3_column_int64(hStmt, 1);
     425         182 :         const int rowId = sqlite3_column_int(hStmt, 2);
     426             : 
     427             :         VFKFeatureSQLite *poFeature =
     428         182 :             (VFKFeatureSQLite *)GetFeatureByIndex(rowId - 1);
     429         182 :         if (poFeature == nullptr || poFeature->GetFID() != iFID)
     430             :         {
     431           0 :             continue;
     432             :         }
     433             : 
     434             :         VFKFeatureSQLite *poLine =
     435         182 :             poDataBlockLines->GetFeature(vrColumn, vrValue, 2, TRUE);
     436             : 
     437             :         const OGRGeometry *poOgrGeometry =
     438         182 :             poLine ? poLine->GetGeometry() : nullptr;
     439         182 :         if (!poOgrGeometry || !poFeature->SetGeometry(poOgrGeometry))
     440             :         {
     441           0 :             CPLDebug("OGR-VFK",
     442             :                      "VFKDataBlockSQLite::LoadGeometryLineStringHP(): name=%s "
     443             :                      "fid=" CPL_FRMT_GIB " "
     444             :                      "id=" CPL_FRMT_GUIB " -> %s geometry",
     445             :                      m_pszName, iFID, vrValue[0],
     446             :                      poOgrGeometry ? "invalid" : "empty");
     447           0 :             nInvalid++;
     448           0 :             continue;
     449             :         }
     450             : 
     451             :         /* store also geometry in DB */
     452         364 :         if (poReader->IsSpatial() &&
     453         182 :             SaveGeometryToDB(poOgrGeometry, rowId) != OGRERR_FAILURE)
     454         182 :             nGeometries++;
     455             :     }
     456             : 
     457             :     /* update number of geometries in VFK_DB_TABLE table */
     458          44 :     UpdateVfkBlocks(nGeometries);
     459             : 
     460          44 :     if (poReader->IsSpatial())
     461          44 :         poReader->ExecuteSQL("COMMIT");
     462             : 
     463          44 :     return nInvalid;
     464             : }
     465             : 
     466             : /*!
     467             :   \brief Load geometry (polygon BUD/PAR layers)
     468             : 
     469             :   \return number of invalid features
     470             : */
     471          30 : int VFKDataBlockSQLite::LoadGeometryPolygon()
     472             : {
     473          30 :     VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
     474             : 
     475          30 :     VFKDataBlockSQLite *poDataBlockLines1 = nullptr;
     476          30 :     VFKDataBlockSQLite *poDataBlockLines2 = nullptr;
     477          30 :     bool bIsPar = false;
     478          30 :     if (EQUAL(m_pszName, "PAR"))
     479             :     {
     480             :         poDataBlockLines1 =
     481          15 :             (VFKDataBlockSQLite *)m_poReader->GetDataBlock("HP");
     482          15 :         poDataBlockLines2 = poDataBlockLines1;
     483          15 :         bIsPar = true;
     484             :     }
     485             :     else
     486             :     {
     487             :         poDataBlockLines1 =
     488          15 :             (VFKDataBlockSQLite *)m_poReader->GetDataBlock("OB");
     489             :         poDataBlockLines2 =
     490          15 :             (VFKDataBlockSQLite *)m_poReader->GetDataBlock("SBP");
     491          15 :         bIsPar = false;
     492             :     }
     493          30 :     if (nullptr == poDataBlockLines1)
     494             :     {
     495           0 :         CPLError(CE_Warning, CPLE_FileIO,
     496             :                  "Data block %s not found. Unable to build geometry for %s.",
     497             :                  bIsPar ? "HP" : "OB", m_pszName);
     498           0 :         return -1;
     499             :     }
     500          30 :     if (nullptr == poDataBlockLines2)
     501             :     {
     502           0 :         CPLError(CE_Warning, CPLE_FileIO,
     503             :                  "Data block %s not found. Unable to build geometry for %s.",
     504             :                  "SBP", m_pszName);
     505           0 :         return -1;
     506             :     }
     507             : 
     508          30 :     poDataBlockLines1->LoadGeometry();
     509          30 :     poDataBlockLines2->LoadGeometry();
     510             : 
     511          30 :     if (LoadGeometryFromDB())  // Try to load geometry from DB.
     512           1 :         return 0;
     513             : 
     514          29 :     const char *vrColumn[2] = {nullptr, nullptr};
     515          29 :     GUIntBig vrValue[2] = {0, 0};
     516          29 :     if (bIsPar)
     517             :     {
     518          14 :         vrColumn[0] = "PAR_ID_1";
     519          14 :         vrColumn[1] = "PAR_ID_2";
     520             :     }
     521             :     else
     522             :     {
     523          15 :         vrColumn[0] = "OB_ID";
     524          15 :         vrColumn[1] = "PORADOVE_CISLO_BODU";
     525          15 :         vrValue[1] = 1;
     526             :     }
     527             : 
     528          58 :     CPLString osSQL;
     529          29 :     osSQL.Printf("SELECT ID,%s,rowid FROM %s", FID_COLUMN, m_pszName);
     530          29 :     sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
     531             : 
     532          29 :     if (poReader->IsSpatial())
     533          29 :         poReader->ExecuteSQL("BEGIN");
     534             : 
     535          58 :     VFKFeatureSQLiteList poLineList;
     536             :     /* first is to be considered as exterior */
     537          58 :     PointListArray poRingList;
     538          58 :     std::vector<OGRLinearRing *> poLinearRingList;
     539          29 :     OGRPolygon ogrPolygon;
     540          29 :     int nInvalidNoLines = 0;
     541          29 :     int nInvalidNoRings = 0;
     542          29 :     int nGeometries = 0;
     543             : 
     544          43 :     while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
     545             :     {
     546             :         /* read values */
     547          14 :         const GUIntBig id = sqlite3_column_int64(hStmt, 0);
     548          14 :         const long iFID = static_cast<long>(sqlite3_column_int64(hStmt, 1));
     549          14 :         const int rowId = sqlite3_column_int(hStmt, 2);
     550             : 
     551             :         VFKFeatureSQLite *poFeature =
     552          14 :             (VFKFeatureSQLite *)GetFeatureByIndex(rowId - 1);
     553          14 :         if (poFeature == nullptr || poFeature->GetFID() != iFID)
     554             :         {
     555           0 :             continue;
     556             :         }
     557             : 
     558          14 :         if (bIsPar)
     559             :         {
     560          14 :             vrValue[0] = vrValue[1] = id;
     561          14 :             poLineList = poDataBlockLines1->GetFeatures(vrColumn, vrValue, 2);
     562             :         }
     563             :         else
     564             :         {
     565             :             // std::vector<VFKFeatureSQLite *> poLineListOb;
     566             : 
     567             :             osSQL.Printf("SELECT ID FROM %s WHERE BUD_ID = " CPL_FRMT_GUIB,
     568           0 :                          poDataBlockLines1->GetName(), id);
     569           0 :             if (poReader->IsSpatial())
     570             :             {
     571           0 :                 CPLString osColumn;
     572             : 
     573           0 :                 osColumn.Printf(" AND %s IS NULL", GEOM_COLUMN);
     574           0 :                 osSQL += osColumn;
     575             :             }
     576           0 :             sqlite3_stmt *hStmtOb = poReader->PrepareStatement(osSQL.c_str());
     577             : 
     578           0 :             while (poReader->ExecuteSQL(hStmtOb) == OGRERR_NONE)
     579             :             {
     580           0 :                 const GUIntBig idOb = sqlite3_column_int64(hStmtOb, 0);
     581           0 :                 vrValue[0] = idOb;
     582             :                 VFKFeatureSQLite *poLineSbp =
     583           0 :                     poDataBlockLines2->GetFeature(vrColumn, vrValue, 2);
     584           0 :                 if (poLineSbp)
     585           0 :                     poLineList.push_back(poLineSbp);
     586             :             }
     587             :         }
     588          14 :         size_t nLines = poLineList.size();
     589          14 :         if (nLines < 1)
     590             :         {
     591           0 :             CPLDebug(
     592             :                 "OGR-VFK",
     593             :                 "%s: unable to collect rings for polygon fid = %ld (no lines)",
     594             :                 m_pszName, iFID);
     595           0 :             nInvalidNoLines++;
     596           0 :             continue;
     597             :         }
     598             : 
     599             :         /* clear */
     600          14 :         ogrPolygon.empty();
     601             : 
     602             :         /* free ring list */
     603          14 :         for (PointListArray::iterator iRing = poRingList.begin(),
     604          14 :                                       eRing = poRingList.end();
     605          14 :              iRing != eRing; ++iRing)
     606             :         {
     607           0 :             delete (*iRing);
     608           0 :             *iRing = nullptr;
     609             :         }
     610          14 :         poRingList.clear();
     611             : 
     612             :         /* collect rings from lines */
     613             : #if 1
     614             :         // Fast version using a map to identify quickly a ring from its end
     615             :         // point.
     616          14 :         std::map<std::pair<double, double>, PointList *> oMapEndRing;
     617          28 :         while (!poLineList.empty())
     618             :         {
     619          14 :             auto pGeom = poLineList.front()->GetGeometry();
     620          14 :             if (pGeom)
     621             :             {
     622          14 :                 auto poLine = pGeom->toLineString();
     623          14 :                 if (poLine == nullptr || poLine->getNumPoints() < 2)
     624           0 :                     continue;
     625          14 :                 poLineList.erase(poLineList.begin());
     626          14 :                 PointList *poList = new PointList();
     627          14 :                 FillPointList(poList, poLine);
     628          14 :                 poRingList.emplace_back(poList);
     629          28 :                 OGRPoint oFirst, oLast;
     630          14 :                 poLine->StartPoint(&oFirst);
     631          14 :                 poLine->EndPoint(&oLast);
     632          14 :                 oMapEndRing[std::pair<double, double>(oLast.getX(),
     633          28 :                                                       oLast.getY())] = poList;
     634             : 
     635          14 :                 bool bWorkDone = true;
     636         182 :                 while (bWorkDone && (*poList).front() != (*poList).back())
     637             :                 {
     638         168 :                     bWorkDone = false;
     639         700 :                     for (auto oIter = poLineList.begin();
     640        1232 :                          oIter != poLineList.end(); ++oIter)
     641             :                     {
     642         700 :                         const auto &oCandidate = *oIter;
     643         700 :                         auto poCandidateGeom = oCandidate->GetGeometry();
     644         700 :                         if (poCandidateGeom == nullptr)
     645           0 :                             continue;
     646         700 :                         poLine = poCandidateGeom->toLineString();
     647         700 :                         if (poLine == nullptr || poLine->getNumPoints() < 2)
     648           0 :                             continue;
     649         700 :                         poLine->StartPoint(&oFirst);
     650         700 :                         poLine->EndPoint(&oLast);
     651             :                         // MER = MapEndRing
     652             :                         auto oIterMER =
     653           0 :                             oMapEndRing.find(std::pair<double, double>(
     654         700 :                                 oFirst.getX(), oFirst.getY()));
     655         700 :                         if (oIterMER != oMapEndRing.end())
     656             :                         {
     657         126 :                             auto ring = oIterMER->second;
     658         252 :                             PointList oList;
     659         126 :                             FillPointList(&oList, poLine);
     660             :                             /* forward, skip first point */
     661         252 :                             ring->insert(ring->end(), oList.begin() + 1,
     662         378 :                                          oList.end());
     663         126 :                             poLineList.erase(oIter);
     664         126 :                             oMapEndRing.erase(oIterMER);
     665           0 :                             oMapEndRing[std::pair<double, double>(
     666         126 :                                 oLast.getX(), oLast.getY())] = poList;
     667         126 :                             bWorkDone = true;
     668         126 :                             break;
     669             :                         }
     670           0 :                         oIterMER = oMapEndRing.find(std::pair<double, double>(
     671         574 :                             oLast.getX(), oLast.getY()));
     672         574 :                         if (oIterMER != oMapEndRing.end())
     673             :                         {
     674          42 :                             auto ring = oIterMER->second;
     675          84 :                             PointList oList;
     676          42 :                             FillPointList(&oList, poLine);
     677             :                             /* backward, skip first point */
     678          42 :                             ring->insert(ring->end(), oList.rbegin() + 1,
     679          84 :                                          oList.rend());
     680          42 :                             poLineList.erase(oIter);
     681          42 :                             oMapEndRing.erase(oIterMER);
     682           0 :                             oMapEndRing[std::pair<double, double>(
     683          42 :                                 oFirst.getX(), oFirst.getY())] = ring;
     684          42 :                             bWorkDone = true;
     685          42 :                             break;
     686             :                         }
     687             :                     }
     688             :                 }
     689             :             }
     690             :         }
     691             : #else
     692             :         bool bFound = false;
     693             :         int nCount = 0;
     694             :         const int nCountMax = static_cast<int>(nLines) * 2;
     695             :         while (!poLineList.empty() && nCount < nCountMax)
     696             :         {
     697             :             bool bNewRing = !bFound;
     698             :             bFound = false;
     699             :             int i = 1;
     700             :             for (VFKFeatureSQLiteList::iterator iHp = poLineList.begin(),
     701             :                                                 eHp = poLineList.end();
     702             :                  iHp != eHp; ++iHp, ++i)
     703             :             {
     704             :                 auto pGeom = (*iHp)->GetGeometry();
     705             :                 if (pGeom && AppendLineToRing(&poRingList,
     706             :                                               pGeom->toLineString(), bNewRing))
     707             :                 {
     708             :                     bFound = true;
     709             :                     poLineList.erase(iHp);
     710             :                     break;
     711             :                 }
     712             :             }
     713             :             nCount++;
     714             :         }
     715             : #endif
     716          14 :         CPLDebug("OGR-VFK", "%s: fid = %ld nlines = %d -> nrings = %d",
     717          14 :                  m_pszName, iFID, (int)nLines, (int)poRingList.size());
     718             : 
     719          14 :         if (!poLineList.empty())
     720             :         {
     721           0 :             CPLDebug("OGR-VFK",
     722             :                      "%s: unable to collect rings for polygon fid = %ld",
     723             :                      m_pszName, iFID);
     724           0 :             nInvalidNoRings++;
     725           0 :             continue;
     726             :         }
     727             : 
     728             :         /* build rings */
     729          14 :         poLinearRingList.clear();
     730          14 :         OGRLinearRing *poOgrRing = nullptr;
     731          28 :         for (PointListArray::const_iterator iRing = poRingList.begin(),
     732          14 :                                             eRing = poRingList.end();
     733          42 :              iRing != eRing; ++iRing)
     734             :         {
     735          14 :             PointList *poList = *iRing;
     736             : 
     737          14 :             poLinearRingList.push_back(new OGRLinearRing());
     738          14 :             poOgrRing = poLinearRingList.back();
     739          14 :             CPLAssert(nullptr != poOgrRing);
     740             : 
     741         210 :             for (PointList::iterator iPoint = poList->begin(),
     742          14 :                                      ePoint = poList->end();
     743         406 :                  iPoint != ePoint; ++iPoint)
     744             :             {
     745         196 :                 OGRPoint *poPoint = &(*iPoint);
     746         196 :                 poOgrRing->addPoint(poPoint);
     747             :             }
     748             :         }
     749             : 
     750             :         /* find exterior ring */
     751          14 :         if (poLinearRingList.size() > 1)
     752             :         {
     753           0 :             std::vector<OGRLinearRing *>::iterator exteriorRing;
     754             : 
     755           0 :             exteriorRing = poLinearRingList.begin();
     756           0 :             double dMaxArea = -1.0;
     757           0 :             for (std::vector<OGRLinearRing *>::iterator
     758           0 :                      iRing = poLinearRingList.begin(),
     759           0 :                      eRing = poLinearRingList.end();
     760           0 :                  iRing != eRing; ++iRing)
     761             :             {
     762           0 :                 poOgrRing = *iRing;
     763           0 :                 if (!IsRingClosed(poOgrRing))
     764           0 :                     continue; /* skip unclosed rings */
     765             : 
     766           0 :                 const double dArea = poOgrRing->get_Area();
     767           0 :                 if (dArea > dMaxArea)
     768             :                 {
     769           0 :                     dMaxArea = dArea;
     770           0 :                     exteriorRing = iRing;
     771             :                 }
     772             :             }
     773           0 :             if (exteriorRing != poLinearRingList.begin())
     774             :             {
     775           0 :                 std::swap(*poLinearRingList.begin(), *exteriorRing);
     776             :             }
     777             :         }
     778             : 
     779             :         /* build polygon from rings */
     780          14 :         int nBridges = 0;
     781          14 :         for (std::vector<OGRLinearRing *>::iterator
     782          14 :                  iRing = poLinearRingList.begin(),
     783          14 :                  eRing = poLinearRingList.end();
     784          28 :              iRing != eRing; ++iRing)
     785             :         {
     786          14 :             poOgrRing = *iRing;
     787             : 
     788             :             /* check if ring is closed */
     789          14 :             if (IsRingClosed(poOgrRing))
     790             :             {
     791          14 :                 ogrPolygon.addRing(poOgrRing);
     792             :             }
     793             :             else
     794             :             {
     795           0 :                 if (poOgrRing->getNumPoints() == 2)
     796             :                 {
     797           0 :                     CPLDebug("OGR-VFK",
     798             :                              "%s: Polygon (fid = %ld) bridge removed",
     799             :                              m_pszName, iFID);
     800           0 :                     nBridges++;
     801             :                 }
     802             :                 else
     803             :                 {
     804           0 :                     CPLDebug("OGR-VFK",
     805             :                              "%s: Polygon (fid = %ld) unclosed ring skipped",
     806             :                              m_pszName, iFID);
     807             :                 }
     808             :             }
     809          14 :             delete poOgrRing;
     810          14 :             *iRing = nullptr;
     811             :         }
     812             : 
     813             :         /* set polygon */
     814          14 :         ogrPolygon.setCoordinateDimension(2); /* force 2D */
     815          14 :         if (ogrPolygon.getNumInteriorRings() + nBridges !=
     816          28 :                 (int)poLinearRingList.size() - 1 ||
     817          14 :             !poFeature->SetGeometry(&ogrPolygon))
     818             :         {
     819           0 :             nInvalidNoRings++;
     820           0 :             continue;
     821             :         }
     822             : 
     823             :         /* store also geometry in DB */
     824          28 :         if (poReader->IsSpatial() &&
     825          14 :             SaveGeometryToDB(&ogrPolygon, rowId) != OGRERR_FAILURE)
     826          14 :             nGeometries++;
     827             :     }
     828             : 
     829             :     /* free ring list */
     830          43 :     for (PointListArray::iterator iRing = poRingList.begin(),
     831          29 :                                   eRing = poRingList.end();
     832          43 :          iRing != eRing; ++iRing)
     833             :     {
     834          14 :         delete (*iRing);
     835          14 :         *iRing = nullptr;
     836             :     }
     837             : 
     838          29 :     CPLDebug("OGR-VFK", "%s: nolines = %d norings = %d", m_pszName,
     839             :              nInvalidNoLines, nInvalidNoRings);
     840             : 
     841             :     /* update number of geometries in VFK_DB_TABLE table */
     842          29 :     UpdateVfkBlocks(nGeometries);
     843             : 
     844          29 :     if (poReader->IsSpatial())
     845          29 :         poReader->ExecuteSQL("COMMIT");
     846             : 
     847          29 :     return nInvalidNoLines + nInvalidNoRings;
     848             : }
     849             : 
     850             : /*!
     851             :   \brief Get feature by FID
     852             : 
     853             :   Modifies next feature id.
     854             : 
     855             :   \param nFID feature id
     856             : 
     857             :   \return pointer to feature definition or NULL on failure (not found)
     858             : */
     859           0 : IVFKFeature *VFKDataBlockSQLite::GetFeature(GIntBig nFID)
     860             : {
     861           0 :     if (m_nFeatureCount < 0)
     862             :     {
     863           0 :         m_poReader->ReadDataRecords(this);
     864             :     }
     865             : 
     866           0 :     if (nFID < 1 || nFID > m_nFeatureCount)
     867           0 :         return nullptr;
     868             : 
     869           0 :     if (m_bGeometryPerBlock && !m_bGeometry)
     870             :     {
     871           0 :         LoadGeometry();
     872             :     }
     873             : 
     874           0 :     VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
     875             : 
     876           0 :     CPLString osSQL;
     877             :     osSQL.Printf("SELECT rowid FROM %s WHERE %s = " CPL_FRMT_GIB, m_pszName,
     878           0 :                  FID_COLUMN, nFID);
     879           0 :     if (EQUAL(m_pszName, "SBP") || EQUAL(m_pszName, "SBPG"))
     880             :     {
     881           0 :         osSQL += " AND PORADOVE_CISLO_BODU = 1";
     882             :     }
     883           0 :     sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
     884             : 
     885           0 :     int rowId = -1;
     886           0 :     if (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
     887             :     {
     888           0 :         rowId = sqlite3_column_int(hStmt, 0);
     889             :     }
     890           0 :     sqlite3_finalize(hStmt);
     891             : 
     892           0 :     return GetFeatureByIndex(rowId - 1);
     893             : }
     894             : 
     895             : /*!
     896             :   \brief Get first found feature based on its property
     897             : 
     898             :   \param column property name
     899             :   \param value property value
     900             :   \param bGeom True to check also geometry != NULL
     901             : 
     902             :   \return pointer to feature definition or NULL on failure (not found)
     903             : */
     904         406 : VFKFeatureSQLite *VFKDataBlockSQLite::GetFeature(const char *column,
     905             :                                                  GUIntBig value, bool bGeom)
     906             : {
     907         406 :     VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
     908             : 
     909         812 :     CPLString osSQL;
     910             :     osSQL.Printf("SELECT %s from %s WHERE %s = " CPL_FRMT_GUIB, FID_COLUMN,
     911         406 :                  m_pszName, column, value);
     912         406 :     if (bGeom)
     913             :     {
     914           0 :         CPLString osColumn;
     915             : 
     916           0 :         osColumn.Printf(" AND %s IS NOT NULL", GEOM_COLUMN);
     917           0 :         osSQL += osColumn;
     918             :     }
     919             : 
     920         406 :     sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
     921         406 :     if (poReader->ExecuteSQL(hStmt) != OGRERR_NONE)
     922           0 :         return nullptr;
     923             : 
     924         406 :     const int idx = sqlite3_column_int(hStmt, 0) - 1;
     925         406 :     sqlite3_finalize(hStmt);
     926             : 
     927         406 :     if (idx < 0 || idx >= m_nFeatureCount)  // ? assert
     928           0 :         return nullptr;
     929             : 
     930         406 :     return (VFKFeatureSQLite *)GetFeatureByIndex(idx);
     931             : }
     932             : 
     933             : /*!
     934             :   \brief Get first found feature based on its properties (AND)
     935             : 
     936             :   \param column array of property names
     937             :   \param value array of property values
     938             :   \param num number of array items
     939             :   \param bGeom True to check also geometry != NULL
     940             : 
     941             :   \return pointer to feature definition or NULL on failure (not found)
     942             : */
     943         182 : VFKFeatureSQLite *VFKDataBlockSQLite::GetFeature(const char **column,
     944             :                                                  GUIntBig *value, int num,
     945             :                                                  bool bGeom)
     946             : {
     947         182 :     VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
     948             : 
     949         364 :     CPLString osSQL;
     950         182 :     osSQL.Printf("SELECT %s FROM %s WHERE ", FID_COLUMN, m_pszName);
     951             : 
     952         364 :     CPLString osItem;
     953         546 :     for (int i = 0; i < num; i++)
     954             :     {
     955         364 :         if (i > 0)
     956         182 :             osItem.Printf(" AND %s = " CPL_FRMT_GUIB, column[i], value[i]);
     957             :         else
     958         182 :             osItem.Printf("%s = " CPL_FRMT_GUIB, column[i], value[i]);
     959         364 :         osSQL += osItem;
     960             :     }
     961         182 :     if (bGeom)
     962             :     {
     963         182 :         osItem.Printf(" AND %s IS NOT NULL", GEOM_COLUMN);
     964         182 :         osSQL += osItem;
     965             :     }
     966             : 
     967         182 :     sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
     968         182 :     if (poReader->ExecuteSQL(hStmt) != OGRERR_NONE)
     969           0 :         return nullptr;
     970             : 
     971         182 :     int idx = sqlite3_column_int(hStmt, 0) - 1; /* rowid starts at 1 */
     972         182 :     sqlite3_finalize(hStmt);
     973             : 
     974         182 :     if (idx < 0 || idx >= m_nFeatureCount)  // ? assert
     975           0 :         return nullptr;
     976             : 
     977         182 :     return (VFKFeatureSQLite *)GetFeatureByIndex(idx);
     978             : }
     979             : 
     980             : /*!
     981             :   \brief Get features based on properties
     982             : 
     983             :   \param column array of property names
     984             :   \param value array of property values
     985             :   \param num number of array items
     986             : 
     987             :   \return list of features
     988             : */
     989          14 : VFKFeatureSQLiteList VFKDataBlockSQLite::GetFeatures(const char **column,
     990             :                                                      GUIntBig *value, int num)
     991             : {
     992          14 :     VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
     993             : 
     994          28 :     CPLString osItem;
     995          28 :     CPLString osSQL;
     996          14 :     osSQL.Printf("SELECT rowid from %s WHERE ", m_pszName);
     997          42 :     for (int i = 0; i < num; i++)
     998             :     {
     999          28 :         if (i > 0)
    1000          14 :             osItem.Printf(" OR %s = " CPL_FRMT_GUIB, column[i], value[i]);
    1001             :         else
    1002          14 :             osItem.Printf("%s = " CPL_FRMT_GUIB, column[i], value[i]);
    1003          28 :         osSQL += osItem;
    1004             :     }
    1005          14 :     osSQL += " ORDER BY ";
    1006          14 :     osSQL += FID_COLUMN;
    1007             : 
    1008          28 :     VFKFeatureSQLiteList fList;
    1009             : 
    1010          14 :     sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
    1011         196 :     while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
    1012             :     {
    1013         182 :         const int iRowId = sqlite3_column_int(hStmt, 0);
    1014             :         VFKFeatureSQLite *poFeature =
    1015         182 :             dynamic_cast<VFKFeatureSQLite *>(GetFeatureByIndex(iRowId - 1));
    1016         182 :         if (poFeature == nullptr)
    1017             :         {
    1018           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot retrieve feature %d",
    1019             :                      iRowId);
    1020           0 :             sqlite3_finalize(hStmt);
    1021           0 :             return VFKFeatureSQLiteList();
    1022             :         }
    1023         182 :         fList.push_back(poFeature);
    1024             :     }
    1025             : 
    1026          14 :     return fList;
    1027             : }
    1028             : 
    1029             : /*!
    1030             :   \brief Save geometry to DB (as WKB)
    1031             : 
    1032             :   \param poGeom pointer to OGRGeometry to be saved
    1033             :   \param iRowId row id to update
    1034             : 
    1035             :   \return OGRERR_NONE on success otherwise OGRERR_FAILURE
    1036             : */
    1037         574 : OGRErr VFKDataBlockSQLite::SaveGeometryToDB(const OGRGeometry *poGeom,
    1038             :                                             int iRowId)
    1039             : {
    1040             :     int rc;
    1041        1148 :     CPLString osSQL;
    1042             : 
    1043         574 :     sqlite3_stmt *hStmt = nullptr;
    1044             : 
    1045         574 :     VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
    1046             : 
    1047             :     /* check if geometry column exists (see SUPPRESS_GEOMETRY open
    1048             :        option) */
    1049         574 :     if (AddGeometryColumn() != OGRERR_NONE)
    1050           0 :         return OGRERR_FAILURE;
    1051             : 
    1052         574 :     if (poGeom)
    1053             :     {
    1054         574 :         const size_t nWKBLen = poGeom->WkbSize();
    1055         574 :         if (nWKBLen > static_cast<size_t>(std::numeric_limits<int>::max()))
    1056             :         {
    1057           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too large geometry");
    1058           0 :             return OGRERR_FAILURE;
    1059             :         }
    1060         574 :         GByte *pabyWKB = (GByte *)VSI_MALLOC_VERBOSE(nWKBLen);
    1061         574 :         if (pabyWKB)
    1062             :         {
    1063         574 :             poGeom->exportToWkb(wkbNDR, pabyWKB);
    1064             : 
    1065             :             osSQL.Printf("UPDATE %s SET %s = ? WHERE rowid = %d", m_pszName,
    1066         574 :                          GEOM_COLUMN, iRowId);
    1067         574 :             hStmt = poReader->PrepareStatement(osSQL.c_str());
    1068             : 
    1069         574 :             rc = sqlite3_bind_blob(hStmt, 1, pabyWKB, static_cast<int>(nWKBLen),
    1070             :                                    CPLFree);
    1071         574 :             if (rc != SQLITE_OK)
    1072             :             {
    1073           0 :                 sqlite3_finalize(hStmt);
    1074           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1075             :                          "Storing geometry in DB failed");
    1076           0 :                 return OGRERR_FAILURE;
    1077             :             }
    1078             :         }
    1079             :     }
    1080             :     else
    1081             :     { /* invalid */
    1082             :         osSQL.Printf("UPDATE %s SET %s = NULL WHERE rowid = %d", m_pszName,
    1083           0 :                      GEOM_COLUMN, iRowId);
    1084           0 :         hStmt = poReader->PrepareStatement(osSQL.c_str());
    1085             :     }
    1086             : 
    1087         574 :     return poReader->ExecuteSQL(hStmt); /* calls sqlite3_finalize() */
    1088             : }
    1089             : 
    1090             : /*!
    1091             :   \brief Load geometry from DB
    1092             : 
    1093             :   \return true if geometry successfully loaded otherwise false
    1094             : */
    1095         180 : bool VFKDataBlockSQLite::LoadGeometryFromDB()
    1096             : {
    1097         180 :     VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
    1098             : 
    1099         180 :     if (!poReader->IsSpatial()) /* check if DB is spatial */
    1100           0 :         return false;
    1101             : 
    1102         360 :     CPLString osSQL;
    1103             :     osSQL.Printf("SELECT num_geometries FROM %s WHERE table_name = '%s'",
    1104         180 :                  VFK_DB_TABLE, m_pszName);
    1105         180 :     sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
    1106         180 :     if (poReader->ExecuteSQL(hStmt) != OGRERR_NONE)
    1107           0 :         return false;
    1108         180 :     const int nGeometries = sqlite3_column_int(hStmt, 0);
    1109         180 :     sqlite3_finalize(hStmt);
    1110             : 
    1111         180 :     if (nGeometries < 1)
    1112         176 :         return false;
    1113             : 
    1114          12 :     const bool bSkipInvalid = EQUAL(m_pszName, "OB") ||
    1115           8 :                               EQUAL(m_pszName, "OP") ||
    1116           4 :                               EQUAL(m_pszName, "OBBP");
    1117             : 
    1118             :     /* load geometry from DB */
    1119             :     osSQL.Printf("SELECT %s,rowid,%s FROM %s ", GEOM_COLUMN, FID_COLUMN,
    1120           4 :                  m_pszName);
    1121           4 :     if (EQUAL(m_pszName, "SBP") || EQUAL(m_pszName, "SBPG"))
    1122           1 :         osSQL += "WHERE PORADOVE_CISLO_BODU = 1 ";
    1123           4 :     osSQL += "ORDER BY ";
    1124           4 :     osSQL += FID_COLUMN;
    1125           4 :     hStmt = poReader->PrepareStatement(osSQL.c_str());
    1126             : 
    1127           4 :     int rowId = 0;
    1128           4 :     int nInvalid = 0;
    1129           4 :     int nGeometriesCount = 0;
    1130             : 
    1131          45 :     while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
    1132             :     {
    1133          41 :         rowId++;  // =sqlite3_column_int(hStmt, 1);
    1134          41 :         const GIntBig iFID = sqlite3_column_int64(hStmt, 2);
    1135             :         VFKFeatureSQLite *poFeature =
    1136          41 :             dynamic_cast<VFKFeatureSQLite *>(GetFeatureByIndex(rowId - 1));
    1137          41 :         if (poFeature == nullptr || poFeature->GetFID() != iFID)
    1138             :         {
    1139           0 :             continue;
    1140             :         }
    1141             : 
    1142             :         // read geometry from DB
    1143          41 :         const int nBytes = sqlite3_column_bytes(hStmt, 0);
    1144          41 :         OGRGeometry *poGeometry = nullptr;
    1145          41 :         if (nBytes > 0 && OGRGeometryFactory::createFromWkb(
    1146             :                               sqlite3_column_blob(hStmt, 0), nullptr,
    1147             :                               &poGeometry, nBytes) == OGRERR_NONE)
    1148             :         {
    1149          41 :             nGeometriesCount++;
    1150          41 :             if (!poFeature->SetGeometry(poGeometry))
    1151             :             {
    1152           0 :                 nInvalid++;
    1153             :             }
    1154          41 :             delete poGeometry;
    1155             :         }
    1156             :         else
    1157             :         {
    1158           0 :             nInvalid++;
    1159             :         }
    1160             :     }
    1161             : 
    1162           4 :     CPLDebug("OGR-VFK", "%s: %d geometries loaded from DB", m_pszName,
    1163             :              nGeometriesCount);
    1164             : 
    1165           4 :     if (nGeometriesCount != nGeometries)
    1166             :     {
    1167           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1168             :                  "%s: %d geometries loaded (should be %d)", m_pszName,
    1169             :                  nGeometriesCount, nGeometries);
    1170             :     }
    1171             : 
    1172           4 :     if (nInvalid > 0 && !bSkipInvalid)
    1173             :     {
    1174           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1175             :                  "%s: %d features with invalid or empty geometry", m_pszName,
    1176             :                  nInvalid);
    1177             :     }
    1178             : 
    1179           4 :     return true;
    1180             : }
    1181             : 
    1182             : /*!
    1183             :   \brief Update VFK_DB_TABLE table
    1184             : 
    1185             :   \param nGeometries number of geometries to update
    1186             : */
    1187         176 : void VFKDataBlockSQLite::UpdateVfkBlocks(int nGeometries)
    1188             : {
    1189         352 :     CPLString osSQL;
    1190             : 
    1191         176 :     VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
    1192             : 
    1193             :     /* update number of features in VFK_DB_TABLE table */
    1194         176 :     const int nFeatCount = (int)GetFeatureCount();
    1195         176 :     if (nFeatCount > 0)
    1196             :     {
    1197             :         osSQL.Printf("UPDATE %s SET num_features = %d WHERE table_name = '%s'",
    1198          56 :                      VFK_DB_TABLE, nFeatCount, m_pszName);
    1199          56 :         poReader->ExecuteSQL(osSQL.c_str());
    1200             :     }
    1201             : 
    1202             :     /* update number of geometries in VFK_DB_TABLE table */
    1203         176 :     if (nGeometries > 0)
    1204             :     {
    1205          56 :         CPLDebug("OGR-VFK",
    1206             :                  "VFKDataBlockSQLite::UpdateVfkBlocks(): name=%s -> "
    1207             :                  "%d geometries saved to internal DB",
    1208             :                  m_pszName, nGeometries);
    1209             : 
    1210             :         osSQL.Printf(
    1211             :             "UPDATE %s SET num_geometries = %d WHERE table_name = '%s'",
    1212          56 :             VFK_DB_TABLE, nGeometries, m_pszName);
    1213          56 :         poReader->ExecuteSQL(osSQL.c_str());
    1214             :     }
    1215         176 : }
    1216             : 
    1217             : /*!
    1218             :   \brief Update feature id (see SBP)
    1219             : 
    1220             :   \param iFID feature id to set up
    1221             :   \param rowId list of rows to update
    1222             : */
    1223         196 : void VFKDataBlockSQLite::UpdateFID(GIntBig iFID, const std::vector<int> &rowId)
    1224             : {
    1225         392 :     CPLString osSQL, osValue;
    1226         196 :     VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
    1227             : 
    1228             :     /* update number of geometries in VFK_DB_TABLE table */
    1229             :     osSQL.Printf("UPDATE %s SET %s = " CPL_FRMT_GIB " WHERE rowid IN (",
    1230         196 :                  m_pszName, FID_COLUMN, iFID);
    1231         602 :     for (size_t i = 0; i < rowId.size(); i++)
    1232             :     {
    1233         406 :         if (i > 0)
    1234         210 :             osValue.Printf(",%d", rowId[i]);
    1235             :         else
    1236         196 :             osValue.Printf("%d", rowId[i]);
    1237         406 :         osSQL += osValue;
    1238             :     }
    1239         196 :     osSQL += ")";
    1240             : 
    1241         196 :     poReader->ExecuteSQL(osSQL.c_str());
    1242         196 : }
    1243             : 
    1244             : /*!
    1245             :   \brief Check is ring is closed
    1246             : 
    1247             :   \param poRing pointer to OGRLinearRing to check
    1248             : 
    1249             :   \return true if closed otherwise false
    1250             : */
    1251          14 : bool VFKDataBlockSQLite::IsRingClosed(const OGRLinearRing *poRing)
    1252             : {
    1253          14 :     const int nPoints = poRing->getNumPoints();
    1254          14 :     if (nPoints < 3)
    1255           0 :         return false;
    1256             : 
    1257          28 :     if (poRing->getX(0) == poRing->getX(nPoints - 1) &&
    1258          14 :         poRing->getY(0) == poRing->getY(nPoints - 1))
    1259          14 :         return true;
    1260             : 
    1261           0 :     return false;
    1262             : }
    1263             : 
    1264             : /*!
    1265             :   \brief Get primary key
    1266             : 
    1267             :   \return property name or NULL
    1268             : */
    1269         168 : const char *VFKDataBlockSQLite::GetKey() const
    1270             : {
    1271         168 :     if (GetPropertyCount() > 1)
    1272             :     {
    1273         168 :         const VFKPropertyDefn *poPropDefn = GetProperty(0);
    1274         168 :         const char *pszKey = poPropDefn->GetName();
    1275         168 :         if (EQUAL(pszKey, "ID"))
    1276         168 :             return pszKey;
    1277             :     }
    1278             : 
    1279           0 :     return nullptr;
    1280             : }
    1281             : 
    1282             : /*!
    1283             :   \brief Get geometry SQL type (for geometry_columns table)
    1284             : 
    1285             :   \return geometry_type as integer
    1286             : */
    1287         915 : int VFKDataBlockSQLite::GetGeometrySQLType() const
    1288             : {
    1289         915 :     if (m_nGeometryType == wkbPolygon)
    1290          28 :         return 3;
    1291         887 :     else if (m_nGeometryType == wkbLineString)
    1292          56 :         return 2;
    1293         831 :     else if (m_nGeometryType == wkbPoint)
    1294          84 :         return 1;
    1295             : 
    1296         747 :     return 0; /* unknown geometry type */
    1297             : }
    1298             : 
    1299             : /*!
    1300             :   \brief Add geometry column into table if not exists
    1301             : 
    1302             :   \return OGRERR_NONE on success otherwise OGRERR_FAILURE
    1303             :  */
    1304         586 : OGRErr VFKDataBlockSQLite::AddGeometryColumn() const
    1305             : {
    1306        1172 :     CPLString osSQL;
    1307             : 
    1308         586 :     VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
    1309             : 
    1310         586 :     osSQL.Printf("SELECT %s FROM %s LIMIT 0", GEOM_COLUMN, m_pszName);
    1311         586 :     if (poReader->ExecuteSQL(osSQL.c_str(), CE_None) == OGRERR_FAILURE)
    1312             :     {
    1313             :         /* query failed, we assume that geometry column not exists */
    1314           0 :         osSQL.Printf("ALTER TABLE %s ADD COLUMN %s blob", m_pszName,
    1315           0 :                      GEOM_COLUMN);
    1316           0 :         return poReader->ExecuteSQL(osSQL.c_str());
    1317             :     }
    1318             : 
    1319         586 :     return OGRERR_NONE;
    1320             : }
    1321             : 
    1322             : /*!
    1323             :   \brief Load feature properties
    1324             : 
    1325             :   Used for sequential access, see OGRVFKLayer:GetNextFeature().
    1326             : 
    1327             :   \return OGRERR_NONE on success otherwise OGRERR_FAILURE
    1328             : */
    1329           4 : OGRErr VFKDataBlockSQLite::LoadProperties()
    1330             : {
    1331           8 :     CPLString osSQL;
    1332             : 
    1333           4 :     if (m_hStmt)
    1334           0 :         sqlite3_finalize(m_hStmt);
    1335             : 
    1336             :     osSQL.Printf("SELECT * FROM %s",  // TODO: where
    1337           4 :                  m_pszName);
    1338           4 :     if (EQUAL(m_pszName, "SBP") || EQUAL(m_pszName, "SBPG"))
    1339           0 :         osSQL += " WHERE PORADOVE_CISLO_BODU = 1";
    1340             : 
    1341           4 :     m_hStmt = ((VFKReaderSQLite *)m_poReader)->PrepareStatement(osSQL.c_str());
    1342             : 
    1343           4 :     if (m_hStmt == nullptr)
    1344           0 :         return OGRERR_FAILURE;
    1345             : 
    1346           4 :     return OGRERR_NONE;
    1347             : }
    1348             : 
    1349             : /*
    1350             :   \brief Clean feature properties for a next run
    1351             : 
    1352             :   \return OGRERR_NONE on success otherwise OGRERR_FAILURE
    1353             : */
    1354         979 : OGRErr VFKDataBlockSQLite::CleanProperties()
    1355             : {
    1356         979 :     if (m_hStmt)
    1357             :     {
    1358           4 :         if (sqlite3_finalize(m_hStmt) != SQLITE_OK)
    1359             :         {
    1360           0 :             m_hStmt = nullptr;
    1361           0 :             return OGRERR_FAILURE;
    1362             :         }
    1363           4 :         m_hStmt = nullptr;
    1364             :     }
    1365             : 
    1366         979 :     return OGRERR_NONE;
    1367             : }

Generated by: LCOV version 1.14