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

Generated by: LCOV version 1.14