LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/osm - ogrosmdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1762 2235 78.8 %
Date: 2024-05-06 22:33:47 Functions: 78 80 97.5 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements OGROSMDataSource class.
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2012-2014, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #include "gpb.h"
      30             : #include "ogr_osm.h"
      31             : 
      32             : #include <cassert>
      33             : #include <cerrno>
      34             : #include <climits>
      35             : #include <cmath>
      36             : #include <cstddef>
      37             : #include <cstdio>
      38             : #include <cstdlib>
      39             : #include <cstring>
      40             : #include <ctime>
      41             : #include <algorithm>
      42             : #include <limits>
      43             : #include <map>
      44             : #include <memory>
      45             : #include <set>
      46             : #include <string>
      47             : #include <unordered_set>
      48             : #include <utility>
      49             : #include <vector>
      50             : 
      51             : #include "cpl_conv.h"
      52             : #include "cpl_error.h"
      53             : #include "cpl_multiproc.h"
      54             : #include "cpl_port.h"
      55             : #include "cpl_progress.h"
      56             : #include "cpl_string.h"
      57             : #include "cpl_time.h"
      58             : #include "cpl_vsi.h"
      59             : #include "ogr_api.h"
      60             : #include "ogr_core.h"
      61             : #include "ogr_feature.h"
      62             : #include "ogr_geometry.h"
      63             : #include "ogr_p.h"
      64             : #include "ogrlayerdecorator.h"
      65             : #include "ogrsf_frmts.h"
      66             : #include "ogrsqliteexecutesql.h"
      67             : #include "osm_parser.h"
      68             : #include "ogr_swq.h"
      69             : #include "sqlite3.h"
      70             : 
      71             : #undef SQLITE_STATIC
      72             : #define SQLITE_STATIC ((sqlite3_destructor_type) nullptr)
      73             : 
      74             : constexpr int LIMIT_IDS_PER_REQUEST = 200;
      75             : 
      76             : constexpr int IDX_LYR_POINTS = 0;
      77             : constexpr int IDX_LYR_LINES = 1;
      78             : constexpr int IDX_LYR_MULTILINESTRINGS = 2;
      79             : constexpr int IDX_LYR_MULTIPOLYGONS = 3;
      80             : constexpr int IDX_LYR_OTHER_RELATIONS = 4;
      81             : 
      82        7488 : static int DBL_TO_INT(double x)
      83             : {
      84        7488 :     return static_cast<int>(floor(x * 1.0e7 + 0.5));
      85             : }
      86             : 
      87        9754 : static double INT_TO_DBL(int x)
      88             : {
      89        9754 :     return x / 1.0e7;
      90             : }
      91             : 
      92             : constexpr unsigned int MAX_COUNT_FOR_TAGS_IN_WAY = 255;  // Must fit on 1 byte.
      93             : 
      94             : constexpr int NODE_PER_BUCKET = 65536;
      95             : 
      96        4630 : static bool VALID_ID_FOR_CUSTOM_INDEXING(GIntBig _id)
      97             : {
      98        4630 :     return _id >= 0 && _id / NODE_PER_BUCKET < INT_MAX;
      99             : }
     100             : 
     101             : // Minimum size of data written on disk, in *uncompressed* case.
     102             : constexpr int SECTOR_SIZE = 512;
     103             : // Which represents, 64 nodes
     104             : // constexpr int NODE_PER_SECTOR = SECTOR_SIZE / (2 * 4);
     105             : constexpr int NODE_PER_SECTOR = 64;
     106             : constexpr int NODE_PER_SECTOR_SHIFT = 6;
     107             : 
     108             : // Per bucket, we keep track of the absence/presence of sectors
     109             : // only, to reduce memory usage.
     110             : // #define BUCKET_BITMAP_SIZE  NODE_PER_BUCKET / (8 * NODE_PER_SECTOR)
     111             : constexpr int BUCKET_BITMAP_SIZE = 128;
     112             : 
     113             : // #define BUCKET_SECTOR_SIZE_ARRAY_SIZE  NODE_PER_BUCKET / NODE_PER_SECTOR
     114             : // Per bucket, we keep track of the real size of the sector. Each sector
     115             : // size is encoded in a single byte, whose value is:
     116             : // (sector_size in bytes - 8 ) / 2, minus 8. 252 means uncompressed
     117             : constexpr int BUCKET_SECTOR_SIZE_ARRAY_SIZE = 1024;
     118             : 
     119             : // Must be a multiple of both BUCKET_BITMAP_SIZE and
     120             : // BUCKET_SECTOR_SIZE_ARRAY_SIZE
     121             : constexpr int knPAGE_SIZE = 4096;
     122             : 
     123             : // compressSize should not be greater than 512, so COMPRESS_SIZE_TO_BYTE() fits
     124             : // on a byte.
     125           1 : static GByte COMPRESS_SIZE_TO_BYTE(size_t nCompressSize)
     126             : {
     127           1 :     return static_cast<GByte>((nCompressSize - 8) / 2);
     128             : }
     129             : 
     130           2 : template <typename T> static T ROUND_COMPRESS_SIZE(T nCompressSize)
     131             : {
     132           2 :     return ((nCompressSize + 1) / 2) * 2;
     133             : }
     134             : 
     135           1 : static int COMPRESS_SIZE_FROM_BYTE(GByte byte_on_size)
     136             : {
     137           1 :     return static_cast<int>(byte_on_size) * 2 + 8;
     138             : }
     139             : 
     140             : // Max number of features that are accumulated in pasWayFeaturePairs.
     141             : constexpr int MAX_DELAYED_FEATURES = 75000;
     142             : // Max number of tags that are accumulated in pasAccumulatedTags.
     143             : constexpr int MAX_ACCUMULATED_TAGS = MAX_DELAYED_FEATURES * 5;
     144             : // Max size of the string with tag values that are accumulated in
     145             : // pabyNonRedundantValues.
     146             : constexpr int MAX_NON_REDUNDANT_VALUES = MAX_DELAYED_FEATURES * 10;
     147             : // Max size of the string with tag values that are accumulated in
     148             : // pabyNonRedundantKeys.
     149             : constexpr int MAX_NON_REDUNDANT_KEYS = MAX_DELAYED_FEATURES * 10;
     150             : // Max number of features that are accumulated in panUnsortedReqIds
     151             : constexpr int MAX_ACCUMULATED_NODES = 1000000;
     152             : 
     153             : #ifdef ENABLE_NODE_LOOKUP_BY_HASHING
     154             : // Size of panHashedIndexes array. Must be in the list at
     155             : // http://planetmath.org/goodhashtableprimes , and greater than
     156             : // MAX_ACCUMULATED_NODES.
     157             : constexpr int HASHED_INDEXES_ARRAY_SIZE = 3145739;
     158             : // #define HASHED_INDEXES_ARRAY_SIZE   1572869
     159             : constexpr int COLLISION_BUCKET_ARRAY_SIZE = (MAX_ACCUMULATED_NODES / 100) * 40;
     160             : 
     161             : // hash function = identity
     162             : #define HASH_ID_FUNC(x) ((GUIntBig)(x))
     163             : #endif  // ENABLE_NODE_LOOKUP_BY_HASHING
     164             : 
     165             : // #define FAKE_LOOKUP_NODES
     166             : 
     167             : // #define DEBUG_MEM_USAGE
     168             : #ifdef DEBUG_MEM_USAGE
     169             : size_t GetMaxTotalAllocs();
     170             : #endif
     171             : 
     172             : static void WriteVarSInt64(GIntBig nSVal, GByte **ppabyData);
     173             : 
     174             : class DSToBeOpened
     175             : {
     176             :   public:
     177             :     GIntBig nPID;
     178             :     CPLString osDSName;
     179             :     CPLString osInterestLayers;
     180             : };
     181             : 
     182             : static CPLMutex *hMutex = nullptr;
     183             : static std::vector<DSToBeOpened> oListDSToBeOpened;
     184             : 
     185             : /************************************************************************/
     186             : /*                    AddInterestLayersForDSName()                      */
     187             : /************************************************************************/
     188             : 
     189           1 : static void AddInterestLayersForDSName(const CPLString &osDSName,
     190             :                                        const CPLString &osInterestLayers)
     191             : {
     192           2 :     CPLMutexHolder oMutexHolder(&hMutex);
     193           2 :     DSToBeOpened oDSToBeOpened;
     194           1 :     oDSToBeOpened.nPID = CPLGetPID();
     195           1 :     oDSToBeOpened.osDSName = osDSName;
     196           1 :     oDSToBeOpened.osInterestLayers = osInterestLayers;
     197           1 :     oListDSToBeOpened.push_back(oDSToBeOpened);
     198           1 : }
     199             : 
     200             : /************************************************************************/
     201             : /*                    GetInterestLayersForDSName()                      */
     202             : /************************************************************************/
     203             : 
     204          31 : static CPLString GetInterestLayersForDSName(const CPLString &osDSName)
     205             : {
     206          62 :     CPLMutexHolder oMutexHolder(&hMutex);
     207          31 :     GIntBig nPID = CPLGetPID();
     208          31 :     for (int i = 0; i < (int)oListDSToBeOpened.size(); i++)
     209             :     {
     210           2 :         if (oListDSToBeOpened[i].nPID == nPID &&
     211           1 :             oListDSToBeOpened[i].osDSName == osDSName)
     212             :         {
     213           2 :             CPLString osInterestLayers = oListDSToBeOpened[i].osInterestLayers;
     214           1 :             oListDSToBeOpened.erase(oListDSToBeOpened.begin() + i);
     215           1 :             return osInterestLayers;
     216             :         }
     217             :     }
     218          30 :     return "";
     219             : }
     220             : 
     221             : /************************************************************************/
     222             : /*                        OGROSMDataSource()                            */
     223             : /************************************************************************/
     224             : 
     225          31 : OGROSMDataSource::OGROSMDataSource()
     226             : {
     227          31 :     m_asKeys.push_back(nullptr);  // guard to avoid index 0 to be used
     228             : 
     229          31 :     MAX_INDEXED_KEYS = static_cast<unsigned>(
     230          31 :         atoi(CPLGetConfigOption("OSM_MAX_INDEXED_KEYS", "32768")));
     231          31 :     MAX_INDEXED_VALUES_PER_KEY = static_cast<unsigned>(
     232          31 :         atoi(CPLGetConfigOption("OSM_MAX_INDEXED_VALUES_PER_KEY", "1024")));
     233          31 : }
     234             : 
     235             : /************************************************************************/
     236             : /*                          ~OGROSMDataSource()                         */
     237             : /************************************************************************/
     238             : 
     239          62 : OGROSMDataSource::~OGROSMDataSource()
     240             : 
     241             : {
     242         186 :     for (int i = 0; i < m_nLayers; i++)
     243         155 :         delete m_papoLayers[i];
     244          31 :     CPLFree(m_papoLayers);
     245             : 
     246          31 :     CPLFree(m_pszName);
     247             : 
     248          31 :     if (m_psParser != nullptr)
     249          31 :         CPLDebug("OSM", "Number of bytes read in file : " CPL_FRMT_GUIB,
     250             :                  OSM_GetBytesRead(m_psParser));
     251          31 :     OSM_Close(m_psParser);
     252             : 
     253          31 :     if (m_hDB != nullptr)
     254          31 :         CloseDB();
     255             : 
     256          31 :     if (m_hDBForComputedAttributes != nullptr)
     257          30 :         sqlite3_close(m_hDBForComputedAttributes);
     258             : 
     259          31 :     if (m_pMyVFS)
     260             :     {
     261          31 :         sqlite3_vfs_unregister(m_pMyVFS);
     262          31 :         CPLFree(m_pMyVFS->pAppData);
     263          31 :         CPLFree(m_pMyVFS);
     264             :     }
     265             : 
     266          31 :     if (!m_osTmpDBName.empty() && m_bMustUnlink)
     267             :     {
     268          31 :         const char *pszVal = CPLGetConfigOption("OSM_UNLINK_TMPFILE", "YES");
     269          31 :         if (!EQUAL(pszVal, "NOT_EVEN_AT_END"))
     270          31 :             VSIUnlink(m_osTmpDBName);
     271             :     }
     272             : 
     273          31 :     CPLFree(m_panReqIds);
     274             : #ifdef ENABLE_NODE_LOOKUP_BY_HASHING
     275          31 :     CPLFree(m_panHashedIndexes);
     276          31 :     CPLFree(m_psCollisionBuckets);
     277             : #endif
     278          31 :     CPLFree(m_pasLonLatArray);
     279          31 :     CPLFree(m_panUnsortedReqIds);
     280             : 
     281          31 :     for (int i = 0; i < m_nWayFeaturePairs; i++)
     282             :     {
     283           0 :         delete m_pasWayFeaturePairs[i].poFeature;
     284             :     }
     285          31 :     CPLFree(m_pasWayFeaturePairs);
     286          31 :     CPLFree(m_pasAccumulatedTags);
     287          31 :     CPLFree(pabyNonRedundantKeys);
     288          31 :     CPLFree(pabyNonRedundantValues);
     289             : 
     290             : #ifdef OSM_DEBUG
     291             :     FILE *f = fopen("keys.txt", "wt");
     292             :     for (int i = 1; i < startic_cast<int>(asKeys.size()); i++)
     293             :     {
     294             :         KeyDesc *psKD = asKeys[i];
     295             :         if (psKD)
     296             :         {
     297             :             fprintf(f, "%08d idx=%d %s\n", psKD->nOccurrences, psKD->nKeyIndex,
     298             :                     psKD->pszK);
     299             :         }
     300             :     }
     301             :     fclose(f);
     302             : #endif
     303             : 
     304          88 :     for (int i = 1; i < static_cast<int>(m_asKeys.size()); i++)
     305             :     {
     306          57 :         KeyDesc *psKD = m_asKeys[i];
     307          57 :         if (psKD)
     308             :         {
     309          56 :             CPLFree(psKD->pszK);
     310         194 :             for (int j = 0; j < static_cast<int>(psKD->asValues.size()); j++)
     311         138 :                 CPLFree(psKD->asValues[j]);
     312          56 :             delete psKD;
     313             :         }
     314             :     }
     315             : 
     316          31 :     if (m_fpNodes)
     317          29 :         VSIFCloseL(m_fpNodes);
     318          31 :     if (!m_osNodesFilename.empty() && m_bMustUnlinkNodesFile)
     319             :     {
     320          29 :         const char *pszVal = CPLGetConfigOption("OSM_UNLINK_TMPFILE", "YES");
     321          29 :         if (!EQUAL(pszVal, "NOT_EVEN_AT_END"))
     322          29 :             VSIUnlink(m_osNodesFilename);
     323             :     }
     324             : 
     325          31 :     CPLFree(m_pabySector);
     326          31 :     std::map<int, Bucket>::iterator oIter = m_oMapBuckets.begin();
     327          54 :     for (; oIter != m_oMapBuckets.end(); ++oIter)
     328             :     {
     329          23 :         if (m_bCompressNodes)
     330             :         {
     331             :             int nRem =
     332           1 :                 oIter->first % (knPAGE_SIZE / BUCKET_SECTOR_SIZE_ARRAY_SIZE);
     333           1 :             if (nRem == 0)
     334           1 :                 CPLFree(oIter->second.u.panSectorSize);
     335             :         }
     336             :         else
     337             :         {
     338          22 :             int nRem = oIter->first % (knPAGE_SIZE / BUCKET_BITMAP_SIZE);
     339          22 :             if (nRem == 0)
     340          22 :                 CPLFree(oIter->second.u.pabyBitmap);
     341             :         }
     342             :     }
     343          62 : }
     344             : 
     345             : /************************************************************************/
     346             : /*                             CloseDB()                               */
     347             : /************************************************************************/
     348             : 
     349          31 : void OGROSMDataSource::CloseDB()
     350             : {
     351          31 :     if (m_hInsertNodeStmt != nullptr)
     352          31 :         sqlite3_finalize(m_hInsertNodeStmt);
     353          31 :     m_hInsertNodeStmt = nullptr;
     354             : 
     355          31 :     if (m_hInsertWayStmt != nullptr)
     356          31 :         sqlite3_finalize(m_hInsertWayStmt);
     357          31 :     m_hInsertWayStmt = nullptr;
     358             : 
     359          31 :     if (m_hInsertPolygonsStandaloneStmt != nullptr)
     360          31 :         sqlite3_finalize(m_hInsertPolygonsStandaloneStmt);
     361          31 :     m_hInsertPolygonsStandaloneStmt = nullptr;
     362             : 
     363          31 :     if (m_hDeletePolygonsStandaloneStmt != nullptr)
     364          31 :         sqlite3_finalize(m_hDeletePolygonsStandaloneStmt);
     365          31 :     m_hDeletePolygonsStandaloneStmt = nullptr;
     366             : 
     367          31 :     if (m_hSelectPolygonsStandaloneStmt != nullptr)
     368          31 :         sqlite3_finalize(m_hSelectPolygonsStandaloneStmt);
     369          31 :     m_hSelectPolygonsStandaloneStmt = nullptr;
     370             : 
     371          31 :     if (m_pahSelectNodeStmt != nullptr)
     372             :     {
     373        6231 :         for (int i = 0; i < LIMIT_IDS_PER_REQUEST; i++)
     374             :         {
     375        6200 :             if (m_pahSelectNodeStmt[i] != nullptr)
     376        6200 :                 sqlite3_finalize(m_pahSelectNodeStmt[i]);
     377             :         }
     378          31 :         CPLFree(m_pahSelectNodeStmt);
     379          31 :         m_pahSelectNodeStmt = nullptr;
     380             :     }
     381             : 
     382          31 :     if (m_pahSelectWayStmt != nullptr)
     383             :     {
     384        6231 :         for (int i = 0; i < LIMIT_IDS_PER_REQUEST; i++)
     385             :         {
     386        6200 :             if (m_pahSelectWayStmt[i] != nullptr)
     387        6200 :                 sqlite3_finalize(m_pahSelectWayStmt[i]);
     388             :         }
     389          31 :         CPLFree(m_pahSelectWayStmt);
     390          31 :         m_pahSelectWayStmt = nullptr;
     391             :     }
     392             : 
     393          31 :     if (m_bInTransaction)
     394          31 :         CommitTransactionCacheDB();
     395             : 
     396          31 :     sqlite3_close(m_hDB);
     397          31 :     m_hDB = nullptr;
     398          31 : }
     399             : 
     400             : /************************************************************************/
     401             : /*                             IndexPoint()                             */
     402             : /************************************************************************/
     403             : 
     404             : constexpr GByte abyBitsCount[] = {
     405             :     0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4,
     406             :     2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
     407             :     2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4,
     408             :     2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
     409             :     2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,
     410             :     4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
     411             :     2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5,
     412             :     3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
     413             :     2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,
     414             :     4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
     415             :     4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8};
     416             : 
     417        3798 : bool OGROSMDataSource::IndexPoint(OSMNode *psNode)
     418             : {
     419        3798 :     if (!m_bIndexPoints)
     420          54 :         return true;
     421             : 
     422        3744 :     if (m_bCustomIndexing)
     423        1984 :         return IndexPointCustom(psNode);
     424             : 
     425        1760 :     return IndexPointSQLite(psNode);
     426             : }
     427             : 
     428             : /************************************************************************/
     429             : /*                          IndexPointSQLite()                          */
     430             : /************************************************************************/
     431             : 
     432        1760 : bool OGROSMDataSource::IndexPointSQLite(OSMNode *psNode)
     433             : {
     434        1760 :     sqlite3_bind_int64(m_hInsertNodeStmt, 1, psNode->nID);
     435             : 
     436             :     LonLat sLonLat;
     437        1760 :     sLonLat.nLon = DBL_TO_INT(psNode->dfLon);
     438        1760 :     sLonLat.nLat = DBL_TO_INT(psNode->dfLat);
     439             : 
     440        1760 :     sqlite3_bind_blob(m_hInsertNodeStmt, 2, &sLonLat, sizeof(sLonLat),
     441             :                       SQLITE_STATIC);
     442             : 
     443        1760 :     const int rc = sqlite3_step(m_hInsertNodeStmt);
     444        1760 :     sqlite3_reset(m_hInsertNodeStmt);
     445        1760 :     if (!(rc == SQLITE_OK || rc == SQLITE_DONE))
     446             :     {
     447           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     448             :                  "Failed inserting node " CPL_FRMT_GIB ": %s", psNode->nID,
     449             :                  sqlite3_errmsg(m_hDB));
     450           0 :         return false;
     451             :     }
     452             : 
     453        1760 :     return true;
     454             : }
     455             : 
     456             : /************************************************************************/
     457             : /*                           FlushCurrentSector()                       */
     458             : /************************************************************************/
     459             : 
     460          53 : bool OGROSMDataSource::FlushCurrentSector()
     461             : {
     462             : #ifndef FAKE_LOOKUP_NODES
     463          53 :     if (m_bCompressNodes)
     464           1 :         return FlushCurrentSectorCompressedCase();
     465             : 
     466          52 :     return FlushCurrentSectorNonCompressedCase();
     467             : #else
     468             :     return true;
     469             : #endif
     470             : }
     471             : 
     472             : /************************************************************************/
     473             : /*                            AllocBucket()                             */
     474             : /************************************************************************/
     475             : 
     476          23 : Bucket *OGROSMDataSource::AllocBucket(int iBucket)
     477             : {
     478          23 :     if (m_bCompressNodes)
     479             :     {
     480           1 :         const int nRem =
     481             :             iBucket % (knPAGE_SIZE / BUCKET_SECTOR_SIZE_ARRAY_SIZE);
     482           1 :         Bucket *psPrevBucket = GetBucket(iBucket - nRem);
     483           1 :         if (psPrevBucket->u.panSectorSize == nullptr)
     484           1 :             psPrevBucket->u.panSectorSize =
     485           1 :                 static_cast<GByte *>(VSI_CALLOC_VERBOSE(1, knPAGE_SIZE));
     486           1 :         GByte *panSectorSize = psPrevBucket->u.panSectorSize;
     487           1 :         Bucket *psBucket = GetBucket(iBucket);
     488           1 :         if (panSectorSize != nullptr)
     489             :         {
     490           1 :             psBucket->u.panSectorSize =
     491           1 :                 panSectorSize + nRem * BUCKET_SECTOR_SIZE_ARRAY_SIZE;
     492           1 :             return psBucket;
     493             :         }
     494           0 :         psBucket->u.panSectorSize = nullptr;
     495             :     }
     496             :     else
     497             :     {
     498          22 :         const int nRem = iBucket % (knPAGE_SIZE / BUCKET_BITMAP_SIZE);
     499          22 :         Bucket *psPrevBucket = GetBucket(iBucket - nRem);
     500          22 :         if (psPrevBucket->u.pabyBitmap == nullptr)
     501          22 :             psPrevBucket->u.pabyBitmap =
     502          22 :                 reinterpret_cast<GByte *>(VSI_CALLOC_VERBOSE(1, knPAGE_SIZE));
     503          22 :         GByte *pabyBitmap = psPrevBucket->u.pabyBitmap;
     504          22 :         Bucket *psBucket = GetBucket(iBucket);
     505          22 :         if (pabyBitmap != nullptr)
     506             :         {
     507          22 :             psBucket->u.pabyBitmap = pabyBitmap + nRem * BUCKET_BITMAP_SIZE;
     508          22 :             return psBucket;
     509             :         }
     510           0 :         psBucket->u.pabyBitmap = nullptr;
     511             :     }
     512             : 
     513             :     // Out of memory.
     514           0 :     CPLError(CE_Failure, CPLE_AppDefined,
     515             :              "AllocBucket() failed. Use OSM_USE_CUSTOM_INDEXING=NO");
     516           0 :     m_bStopParsing = true;
     517           0 :     return nullptr;
     518             : }
     519             : 
     520             : /************************************************************************/
     521             : /*                             GetBucket()                              */
     522             : /************************************************************************/
     523             : 
     524        2031 : Bucket *OGROSMDataSource::GetBucket(int nBucketId)
     525             : {
     526        2031 :     std::map<int, Bucket>::iterator oIter = m_oMapBuckets.find(nBucketId);
     527        2031 :     if (oIter == m_oMapBuckets.end())
     528             :     {
     529          23 :         Bucket *psBucket = &m_oMapBuckets[nBucketId];
     530          23 :         psBucket->nOff = -1;
     531          23 :         if (m_bCompressNodes)
     532           1 :             psBucket->u.panSectorSize = nullptr;
     533             :         else
     534          22 :             psBucket->u.pabyBitmap = nullptr;
     535          23 :         return psBucket;
     536             :     }
     537        2008 :     return &(oIter->second);
     538             : }
     539             : 
     540             : /************************************************************************/
     541             : /*                     FlushCurrentSectorCompressedCase()               */
     542             : /************************************************************************/
     543             : 
     544           1 : bool OGROSMDataSource::FlushCurrentSectorCompressedCase()
     545             : {
     546             :     GByte abyOutBuffer[2 * SECTOR_SIZE];
     547           1 :     GByte *pabyOut = abyOutBuffer;
     548           1 :     LonLat *pasLonLatIn = (LonLat *)m_pabySector;
     549           1 :     int nLastLon = 0;
     550           1 :     int nLastLat = 0;
     551           1 :     bool bLastValid = false;
     552             : 
     553             :     CPLAssert((NODE_PER_SECTOR % 8) == 0);
     554           1 :     memset(abyOutBuffer, 0, NODE_PER_SECTOR / 8);
     555           1 :     pabyOut += NODE_PER_SECTOR / 8;
     556          65 :     for (int i = 0; i < NODE_PER_SECTOR; i++)
     557             :     {
     558          64 :         if (pasLonLatIn[i].nLon || pasLonLatIn[i].nLat)
     559             :         {
     560           9 :             abyOutBuffer[i >> 3] |= (1 << (i % 8));
     561           9 :             if (bLastValid)
     562             :             {
     563           8 :                 const GIntBig nDiff64Lon =
     564           8 :                     static_cast<GIntBig>(pasLonLatIn[i].nLon) -
     565           8 :                     static_cast<GIntBig>(nLastLon);
     566           8 :                 const GIntBig nDiff64Lat = pasLonLatIn[i].nLat - nLastLat;
     567           8 :                 WriteVarSInt64(nDiff64Lon, &pabyOut);
     568           8 :                 WriteVarSInt64(nDiff64Lat, &pabyOut);
     569             :             }
     570             :             else
     571             :             {
     572           1 :                 memcpy(pabyOut, &pasLonLatIn[i], sizeof(LonLat));
     573           1 :                 pabyOut += sizeof(LonLat);
     574             :             }
     575           9 :             bLastValid = true;
     576             : 
     577           9 :             nLastLon = pasLonLatIn[i].nLon;
     578           9 :             nLastLat = pasLonLatIn[i].nLat;
     579             :         }
     580             :     }
     581             : 
     582           1 :     size_t nCompressSize = static_cast<size_t>(pabyOut - abyOutBuffer);
     583           1 :     CPLAssert(nCompressSize < sizeof(abyOutBuffer) - 1);
     584           1 :     abyOutBuffer[nCompressSize] = 0;
     585             : 
     586           1 :     nCompressSize = ROUND_COMPRESS_SIZE(nCompressSize);
     587           1 :     GByte *pabyToWrite = nullptr;
     588           1 :     if (nCompressSize >= static_cast<size_t>(SECTOR_SIZE))
     589             :     {
     590           0 :         nCompressSize = SECTOR_SIZE;
     591           0 :         pabyToWrite = m_pabySector;
     592             :     }
     593             :     else
     594           1 :         pabyToWrite = abyOutBuffer;
     595             : 
     596           1 :     if (VSIFWriteL(pabyToWrite, 1, nCompressSize, m_fpNodes) == nCompressSize)
     597             :     {
     598           1 :         memset(m_pabySector, 0, SECTOR_SIZE);
     599           1 :         m_nNodesFileSize += nCompressSize;
     600             : 
     601           1 :         Bucket *psBucket = GetBucket(m_nBucketOld);
     602           1 :         if (psBucket->u.panSectorSize == nullptr)
     603             :         {
     604           1 :             psBucket = AllocBucket(m_nBucketOld);
     605           1 :             if (psBucket == nullptr)
     606           0 :                 return false;
     607             :         }
     608           1 :         CPLAssert(psBucket->u.panSectorSize != nullptr);
     609           2 :         psBucket->u.panSectorSize[m_nOffInBucketReducedOld] =
     610           1 :             COMPRESS_SIZE_TO_BYTE(nCompressSize);
     611             : 
     612           1 :         return true;
     613             :     }
     614             : 
     615           0 :     CPLError(CE_Failure, CPLE_AppDefined,
     616             :              "Cannot write in temporary node file %s : %s",
     617           0 :              m_osNodesFilename.c_str(), VSIStrerror(errno));
     618             : 
     619           0 :     return false;
     620             : }
     621             : 
     622             : /************************************************************************/
     623             : /*                   FlushCurrentSectorNonCompressedCase()              */
     624             : /************************************************************************/
     625             : 
     626          52 : bool OGROSMDataSource::FlushCurrentSectorNonCompressedCase()
     627             : {
     628          52 :     if (VSIFWriteL(m_pabySector, 1, static_cast<size_t>(SECTOR_SIZE),
     629          52 :                    m_fpNodes) == static_cast<size_t>(SECTOR_SIZE))
     630             :     {
     631          52 :         memset(m_pabySector, 0, SECTOR_SIZE);
     632          52 :         m_nNodesFileSize += SECTOR_SIZE;
     633          52 :         return true;
     634             :     }
     635             : 
     636           0 :     CPLError(CE_Failure, CPLE_AppDefined,
     637             :              "Cannot write in temporary node file %s : %s",
     638           0 :              m_osNodesFilename.c_str(), VSIStrerror(errno));
     639             : 
     640           0 :     return false;
     641             : }
     642             : 
     643             : /************************************************************************/
     644             : /*                          IndexPointCustom()                          */
     645             : /************************************************************************/
     646             : 
     647        1984 : bool OGROSMDataSource::IndexPointCustom(OSMNode *psNode)
     648             : {
     649        1984 :     if (psNode->nID <= m_nPrevNodeId)
     650             :     {
     651           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     652             :                  "Non increasing node id. Use OSM_USE_CUSTOM_INDEXING=NO");
     653           0 :         m_bStopParsing = true;
     654           0 :         return false;
     655             :     }
     656        1984 :     if (!VALID_ID_FOR_CUSTOM_INDEXING(psNode->nID))
     657             :     {
     658           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     659             :                  "Unsupported node id value (" CPL_FRMT_GIB
     660             :                  "). Use OSM_USE_CUSTOM_INDEXING=NO",
     661             :                  psNode->nID);
     662           0 :         m_bStopParsing = true;
     663           0 :         return false;
     664             :     }
     665             : 
     666        1984 :     const int nBucket = static_cast<int>(psNode->nID / NODE_PER_BUCKET);
     667        1984 :     const int nOffInBucket = static_cast<int>(psNode->nID % NODE_PER_BUCKET);
     668        1984 :     const int nOffInBucketReduced = nOffInBucket >> NODE_PER_SECTOR_SHIFT;
     669        1984 :     const int nOffInBucketReducedRemainder =
     670             :         nOffInBucket & ((1 << NODE_PER_SECTOR_SHIFT) - 1);
     671             : 
     672        1984 :     Bucket *psBucket = GetBucket(nBucket);
     673             : 
     674        1984 :     if (!m_bCompressNodes)
     675             :     {
     676        1975 :         const int nBitmapIndex = nOffInBucketReduced / 8;
     677        1975 :         const int nBitmapRemainder = nOffInBucketReduced % 8;
     678        1975 :         if (psBucket->u.pabyBitmap == nullptr)
     679             :         {
     680          22 :             psBucket = AllocBucket(nBucket);
     681          22 :             if (psBucket == nullptr)
     682           0 :                 return false;
     683             :         }
     684        1975 :         CPLAssert(psBucket->u.pabyBitmap != nullptr);
     685        1975 :         psBucket->u.pabyBitmap[nBitmapIndex] |= (1 << nBitmapRemainder);
     686             :     }
     687             : 
     688        1984 :     if (nBucket != m_nBucketOld)
     689             :     {
     690          32 :         CPLAssert(nBucket > m_nBucketOld);
     691          32 :         if (m_nBucketOld >= 0)
     692             :         {
     693           1 :             if (!FlushCurrentSector())
     694             :             {
     695           0 :                 m_bStopParsing = true;
     696           0 :                 return false;
     697             :             }
     698             :         }
     699          32 :         m_nBucketOld = nBucket;
     700          32 :         m_nOffInBucketReducedOld = nOffInBucketReduced;
     701          32 :         CPLAssert(psBucket->nOff == -1);
     702          32 :         psBucket->nOff = VSIFTellL(m_fpNodes);
     703             :     }
     704        1952 :     else if (nOffInBucketReduced != m_nOffInBucketReducedOld)
     705             :     {
     706          26 :         CPLAssert(nOffInBucketReduced > m_nOffInBucketReducedOld);
     707          26 :         if (!FlushCurrentSector())
     708             :         {
     709           0 :             m_bStopParsing = true;
     710           0 :             return false;
     711             :         }
     712          26 :         m_nOffInBucketReducedOld = nOffInBucketReduced;
     713             :     }
     714             : 
     715        1984 :     LonLat *psLonLat = reinterpret_cast<LonLat *>(
     716        1984 :         m_pabySector + sizeof(LonLat) * nOffInBucketReducedRemainder);
     717        1984 :     psLonLat->nLon = DBL_TO_INT(psNode->dfLon);
     718        1984 :     psLonLat->nLat = DBL_TO_INT(psNode->dfLat);
     719             : 
     720        1984 :     m_nPrevNodeId = psNode->nID;
     721             : 
     722        1984 :     return true;
     723             : }
     724             : 
     725             : /************************************************************************/
     726             : /*                             NotifyNodes()                            */
     727             : /************************************************************************/
     728             : 
     729          75 : void OGROSMDataSource::NotifyNodes(unsigned int nNodes, OSMNode *pasNodes)
     730             : {
     731             :     const OGREnvelope *psEnvelope =
     732          75 :         m_papoLayers[IDX_LYR_POINTS]->GetSpatialFilterEnvelope();
     733             : 
     734        3882 :     for (unsigned int i = 0; i < nNodes; i++)
     735             :     {
     736             :         /* If the point doesn't fit into the envelope of the spatial filter */
     737             :         /* then skip it */
     738        3807 :         if (psEnvelope != nullptr && !(pasNodes[i].dfLon >= psEnvelope->MinX &&
     739           9 :                                        pasNodes[i].dfLon <= psEnvelope->MaxX &&
     740           0 :                                        pasNodes[i].dfLat >= psEnvelope->MinY &&
     741           0 :                                        pasNodes[i].dfLat <= psEnvelope->MaxY))
     742           9 :             continue;
     743             : 
     744        3798 :         if (!IndexPoint(&pasNodes[i]))
     745           0 :             break;
     746             : 
     747        3798 :         if (!m_papoLayers[IDX_LYR_POINTS]->IsUserInterested())
     748          84 :             continue;
     749             : 
     750        3714 :         bool bInterestingTag = m_bReportAllNodes;
     751        3714 :         OSMTag *pasTags = pasNodes[i].pasTags;
     752             : 
     753        3714 :         if (!m_bReportAllNodes)
     754             :         {
     755        3720 :             for (unsigned int j = 0; j < pasNodes[i].nTags; j++)
     756             :             {
     757          62 :                 const char *pszK = pasTags[j].pszK;
     758          62 :                 if (m_papoLayers[IDX_LYR_POINTS]->IsSignificantKey(pszK))
     759             :                 {
     760          56 :                     bInterestingTag = true;
     761          56 :                     break;
     762             :                 }
     763             :             }
     764             :         }
     765             : 
     766        3714 :         if (bInterestingTag)
     767             :         {
     768             :             OGRFeature *poFeature =
     769          56 :                 new OGRFeature(m_papoLayers[IDX_LYR_POINTS]->GetLayerDefn());
     770             : 
     771          56 :             poFeature->SetGeometryDirectly(
     772          56 :                 new OGRPoint(pasNodes[i].dfLon, pasNodes[i].dfLat));
     773             : 
     774          56 :             m_papoLayers[IDX_LYR_POINTS]->SetFieldsFromTags(
     775          56 :                 poFeature, pasNodes[i].nID, false, pasNodes[i].nTags, pasTags,
     776          56 :                 &pasNodes[i].sInfo);
     777             : 
     778          56 :             int bFilteredOut = FALSE;
     779          56 :             if (!m_papoLayers[IDX_LYR_POINTS]->AddFeature(
     780          56 :                     poFeature, FALSE, &bFilteredOut, !m_bFeatureAdded))
     781             :             {
     782           0 :                 m_bStopParsing = true;
     783           0 :                 break;
     784             :             }
     785          56 :             else if (!bFilteredOut)
     786          27 :                 m_bFeatureAdded = true;
     787             :         }
     788             :     }
     789          75 : }
     790             : 
     791          75 : static void OGROSMNotifyNodes(unsigned int nNodes, OSMNode *pasNodes,
     792             :                               OSMContext * /* psOSMContext */, void *user_data)
     793             : {
     794          75 :     static_cast<OGROSMDataSource *>(user_data)->NotifyNodes(nNodes, pasNodes);
     795          75 : }
     796             : 
     797             : /************************************************************************/
     798             : /*                            LookupNodes()                             */
     799             : /************************************************************************/
     800             : 
     801             : // #define DEBUG_COLLISIONS 1
     802             : 
     803          44 : void OGROSMDataSource::LookupNodes()
     804             : {
     805          44 :     if (m_bCustomIndexing)
     806          42 :         LookupNodesCustom();
     807             :     else
     808           2 :         LookupNodesSQLite();
     809             : 
     810             : #ifdef ENABLE_NODE_LOOKUP_BY_HASHING
     811          44 :     if (m_nReqIds > 1 && m_bEnableHashedIndex)
     812             :     {
     813          28 :         memset(m_panHashedIndexes, 0xFF,
     814             :                HASHED_INDEXES_ARRAY_SIZE * sizeof(int));
     815          28 :         m_bHashedIndexValid = true;
     816             : #ifdef DEBUG_COLLISIONS
     817             :         int nCollisions = 0;
     818             : #endif
     819          28 :         int iNextFreeBucket = 0;
     820        3699 :         for (unsigned int i = 0; i < m_nReqIds; i++)
     821             :         {
     822        3671 :             int nIndInHashArray = static_cast<int>(
     823        3671 :                 HASH_ID_FUNC(m_panReqIds[i]) % HASHED_INDEXES_ARRAY_SIZE);
     824        3671 :             int nIdx = m_panHashedIndexes[nIndInHashArray];
     825        3671 :             if (nIdx == -1)
     826             :             {
     827        3671 :                 m_panHashedIndexes[nIndInHashArray] = i;
     828             :             }
     829             :             else
     830             :             {
     831             : #ifdef DEBUG_COLLISIONS
     832             :                 nCollisions++;
     833             : #endif
     834           0 :                 int iBucket = 0;
     835           0 :                 if (nIdx >= 0)
     836             :                 {
     837           0 :                     if (iNextFreeBucket == COLLISION_BUCKET_ARRAY_SIZE)
     838             :                     {
     839           0 :                         CPLDebug(
     840             :                             "OSM",
     841             :                             "Too many collisions. Disabling hashed indexing");
     842           0 :                         m_bHashedIndexValid = false;
     843           0 :                         m_bEnableHashedIndex = false;
     844           0 :                         break;
     845             :                     }
     846           0 :                     iBucket = iNextFreeBucket;
     847           0 :                     m_psCollisionBuckets[iNextFreeBucket].nInd = nIdx;
     848           0 :                     m_psCollisionBuckets[iNextFreeBucket].nNext = -1;
     849           0 :                     m_panHashedIndexes[nIndInHashArray] = -iNextFreeBucket - 2;
     850           0 :                     iNextFreeBucket++;
     851             :                 }
     852             :                 else
     853             :                 {
     854           0 :                     iBucket = -nIdx - 2;
     855             :                 }
     856           0 :                 if (iNextFreeBucket == COLLISION_BUCKET_ARRAY_SIZE)
     857             :                 {
     858           0 :                     CPLDebug("OSM",
     859             :                              "Too many collisions. Disabling hashed indexing");
     860           0 :                     m_bHashedIndexValid = false;
     861           0 :                     m_bEnableHashedIndex = false;
     862           0 :                     break;
     863             :                 }
     864             :                 while (true)
     865             :                 {
     866           0 :                     int iNext = m_psCollisionBuckets[iBucket].nNext;
     867           0 :                     if (iNext < 0)
     868             :                     {
     869           0 :                         m_psCollisionBuckets[iBucket].nNext = iNextFreeBucket;
     870           0 :                         m_psCollisionBuckets[iNextFreeBucket].nInd = i;
     871           0 :                         m_psCollisionBuckets[iNextFreeBucket].nNext = -1;
     872           0 :                         iNextFreeBucket++;
     873           0 :                         break;
     874             :                     }
     875           0 :                     iBucket = iNext;
     876           0 :                 }
     877             :             }
     878          28 :         }
     879             : #ifdef DEBUG_COLLISIONS
     880             :         /* Collision rate in practice is around 12% on France, Germany, ... */
     881             :         /* Maximum seen ~ 15.9% on a planet file but often much smaller. */
     882             :         CPLDebug("OSM",
     883             :                  "nCollisions = %d/%d (%.1f %%), iNextFreeBucket = %d/%d",
     884             :                  nCollisions, nReqIds, nCollisions * 100.0 / nReqIds,
     885             :                  iNextFreeBucket, COLLISION_BUCKET_ARRAY_SIZE);
     886             : #endif
     887             :     }
     888             :     else
     889          16 :         m_bHashedIndexValid = false;
     890             : #endif  // ENABLE_NODE_LOOKUP_BY_HASHING
     891          44 : }
     892             : 
     893             : /************************************************************************/
     894             : /*                           LookupNodesSQLite()                        */
     895             : /************************************************************************/
     896             : 
     897           2 : void OGROSMDataSource::LookupNodesSQLite()
     898             : {
     899           2 :     CPLAssert(m_nUnsortedReqIds <=
     900             :               static_cast<unsigned int>(MAX_ACCUMULATED_NODES));
     901             : 
     902           2 :     m_nReqIds = 0;
     903        2044 :     for (unsigned int i = 0; i < m_nUnsortedReqIds; i++)
     904             :     {
     905        2042 :         GIntBig id = m_panUnsortedReqIds[i];
     906        2042 :         m_panReqIds[m_nReqIds++] = id;
     907             :     }
     908             : 
     909           2 :     std::sort(m_panReqIds, m_panReqIds + m_nReqIds);
     910             : 
     911             :     /* Remove duplicates */
     912           2 :     unsigned int j = 0;
     913        2044 :     for (unsigned int i = 0; i < m_nReqIds; i++)
     914             :     {
     915        2042 :         if (!(i > 0 && m_panReqIds[i] == m_panReqIds[i - 1]))
     916        1751 :             m_panReqIds[j++] = m_panReqIds[i];
     917             :     }
     918           2 :     m_nReqIds = j;
     919             : 
     920           2 :     unsigned int iCur = 0;
     921           2 :     j = 0;
     922          12 :     while (iCur < m_nReqIds)
     923             :     {
     924          10 :         unsigned int nToQuery = m_nReqIds - iCur;
     925          10 :         if (nToQuery > static_cast<unsigned int>(LIMIT_IDS_PER_REQUEST))
     926           8 :             nToQuery = static_cast<unsigned int>(LIMIT_IDS_PER_REQUEST);
     927             : 
     928          10 :         sqlite3_stmt *hStmt = m_pahSelectNodeStmt[nToQuery - 1];
     929        1761 :         for (unsigned int i = iCur; i < iCur + nToQuery; i++)
     930             :         {
     931        1751 :             sqlite3_bind_int64(hStmt, i - iCur + 1, m_panReqIds[i]);
     932             :         }
     933          10 :         iCur += nToQuery;
     934             : 
     935        1757 :         while (sqlite3_step(hStmt) == SQLITE_ROW)
     936             :         {
     937        1747 :             const GIntBig id = sqlite3_column_int64(hStmt, 0);
     938        1747 :             LonLat *psLonLat = (LonLat *)sqlite3_column_blob(hStmt, 1);
     939             : 
     940        1747 :             m_panReqIds[j] = id;
     941        1747 :             m_pasLonLatArray[j].nLon = psLonLat->nLon;
     942        1747 :             m_pasLonLatArray[j].nLat = psLonLat->nLat;
     943        1747 :             j++;
     944             :         }
     945             : 
     946          10 :         sqlite3_reset(hStmt);
     947             :     }
     948           2 :     m_nReqIds = j;
     949           2 : }
     950             : 
     951             : /************************************************************************/
     952             : /*                           DecompressSector()                         */
     953             : /************************************************************************/
     954             : 
     955           1 : static bool DecompressSector(const GByte *pabyIn, int nSectorSize,
     956             :                              GByte *pabyOut)
     957             : {
     958           1 :     const GByte *pabyPtr = pabyIn;
     959           1 :     LonLat *pasLonLatOut = (LonLat *)pabyOut;
     960           1 :     int nLastLon = 0;
     961           1 :     int nLastLat = 0;
     962           1 :     bool bLastValid = false;
     963             : 
     964           1 :     pabyPtr += NODE_PER_SECTOR / 8;
     965          65 :     for (int i = 0; i < NODE_PER_SECTOR; i++)
     966             :     {
     967          64 :         if (pabyIn[i >> 3] & (1 << (i % 8)))
     968             :         {
     969           9 :             if (bLastValid)
     970             :             {
     971           8 :                 pasLonLatOut[i].nLon =
     972           8 :                     (int)(nLastLon + ReadVarSInt64(&pabyPtr));
     973           8 :                 pasLonLatOut[i].nLat =
     974           8 :                     (int)(nLastLat + ReadVarSInt64(&pabyPtr));
     975             :             }
     976             :             else
     977             :             {
     978           1 :                 bLastValid = true;
     979           1 :                 memcpy(&(pasLonLatOut[i]), pabyPtr, sizeof(LonLat));
     980           1 :                 pabyPtr += sizeof(LonLat);
     981             :             }
     982             : 
     983           9 :             nLastLon = pasLonLatOut[i].nLon;
     984           9 :             nLastLat = pasLonLatOut[i].nLat;
     985             :         }
     986             :         else
     987             :         {
     988          55 :             pasLonLatOut[i].nLon = 0;
     989          55 :             pasLonLatOut[i].nLat = 0;
     990             :         }
     991             :     }
     992             : 
     993           1 :     int nRead = (int)(pabyPtr - pabyIn);
     994           1 :     nRead = ROUND_COMPRESS_SIZE(nRead);
     995           1 :     return nRead == nSectorSize;
     996             : }
     997             : 
     998             : /************************************************************************/
     999             : /*                           LookupNodesCustom()                        */
    1000             : /************************************************************************/
    1001             : 
    1002          42 : void OGROSMDataSource::LookupNodesCustom()
    1003             : {
    1004          42 :     m_nReqIds = 0;
    1005             : 
    1006          42 :     if (m_nBucketOld >= 0)
    1007             :     {
    1008          26 :         if (!FlushCurrentSector())
    1009             :         {
    1010           0 :             m_bStopParsing = true;
    1011           0 :             return;
    1012             :         }
    1013             : 
    1014          26 :         m_nBucketOld = -1;
    1015             :     }
    1016             : 
    1017          42 :     CPLAssert(m_nUnsortedReqIds <=
    1018             :               static_cast<unsigned int>(MAX_ACCUMULATED_NODES));
    1019             : 
    1020        2688 :     for (unsigned int i = 0; i < m_nUnsortedReqIds; i++)
    1021             :     {
    1022        2646 :         GIntBig id = m_panUnsortedReqIds[i];
    1023             : 
    1024        2646 :         if (!VALID_ID_FOR_CUSTOM_INDEXING(id))
    1025         142 :             continue;
    1026             : 
    1027        2646 :         int nBucket = static_cast<int>(id / NODE_PER_BUCKET);
    1028        2646 :         int nOffInBucket = static_cast<int>(id % NODE_PER_BUCKET);
    1029        2646 :         int nOffInBucketReduced = nOffInBucket >> NODE_PER_SECTOR_SHIFT;
    1030             : 
    1031             :         std::map<int, Bucket>::const_iterator oIter =
    1032        2646 :             m_oMapBuckets.find(nBucket);
    1033        2646 :         if (oIter == m_oMapBuckets.end())
    1034          27 :             continue;
    1035        2619 :         const Bucket *psBucket = &(oIter->second);
    1036             : 
    1037        2619 :         if (m_bCompressNodes)
    1038             :         {
    1039          26 :             if (psBucket->u.panSectorSize == nullptr ||
    1040          26 :                 !(psBucket->u.panSectorSize[nOffInBucketReduced]))
    1041           5 :                 continue;
    1042             :         }
    1043             :         else
    1044             :         {
    1045        2593 :             int nBitmapIndex = nOffInBucketReduced / 8;
    1046        2593 :             int nBitmapRemainder = nOffInBucketReduced % 8;
    1047        2593 :             if (psBucket->u.pabyBitmap == nullptr ||
    1048        2593 :                 !(psBucket->u.pabyBitmap[nBitmapIndex] &
    1049             :                   (1 << nBitmapRemainder)))
    1050         110 :                 continue;
    1051             :         }
    1052             : 
    1053        2504 :         m_panReqIds[m_nReqIds++] = id;
    1054             :     }
    1055             : 
    1056          42 :     std::sort(m_panReqIds, m_panReqIds + m_nReqIds);
    1057             : 
    1058             :     /* Remove duplicates */
    1059          42 :     unsigned int j = 0;  // Used after for.
    1060        2546 :     for (unsigned int i = 0; i < m_nReqIds; i++)
    1061             :     {
    1062        2504 :         if (!(i > 0 && m_panReqIds[i] == m_panReqIds[i - 1]))
    1063        1938 :             m_panReqIds[j++] = m_panReqIds[i];
    1064             :     }
    1065          42 :     m_nReqIds = j;
    1066             : 
    1067             : #ifdef FAKE_LOOKUP_NODES
    1068             :     for (unsigned int i = 0; i < nReqIds; i++)
    1069             :     {
    1070             :         pasLonLatArray[i].nLon = 0;
    1071             :         pasLonLatArray[i].nLat = 0;
    1072             :     }
    1073             : #else
    1074          42 :     if (m_bCompressNodes)
    1075           1 :         LookupNodesCustomCompressedCase();
    1076             :     else
    1077          41 :         LookupNodesCustomNonCompressedCase();
    1078             : #endif
    1079             : }
    1080             : 
    1081             : /************************************************************************/
    1082             : /*                      LookupNodesCustomCompressedCase()               */
    1083             : /************************************************************************/
    1084             : 
    1085           1 : void OGROSMDataSource::LookupNodesCustomCompressedCase()
    1086             : {
    1087           1 :     constexpr int SECURITY_MARGIN = 8 + 8 + 2 * NODE_PER_SECTOR;
    1088             :     GByte abyRawSector[SECTOR_SIZE + SECURITY_MARGIN];
    1089           1 :     memset(abyRawSector + SECTOR_SIZE, 0, SECURITY_MARGIN);
    1090             : 
    1091           1 :     int l_nBucketOld = -1;
    1092           1 :     int l_nOffInBucketReducedOld = -1;
    1093           1 :     int k = 0;
    1094           1 :     int nOffFromBucketStart = 0;
    1095             : 
    1096           1 :     unsigned int j = 0;  // Used after for.
    1097           9 :     for (unsigned int i = 0; i < m_nReqIds; i++)
    1098             :     {
    1099           8 :         const GIntBig id = m_panReqIds[i];
    1100           8 :         const int nBucket = static_cast<int>(id / NODE_PER_BUCKET);
    1101           8 :         const int nOffInBucket = static_cast<int>(id % NODE_PER_BUCKET);
    1102           8 :         const int nOffInBucketReduced = nOffInBucket >> NODE_PER_SECTOR_SHIFT;
    1103           8 :         const int nOffInBucketReducedRemainder =
    1104             :             nOffInBucket & ((1 << NODE_PER_SECTOR_SHIFT) - 1);
    1105             : 
    1106           8 :         if (nBucket != l_nBucketOld)
    1107             :         {
    1108           1 :             l_nOffInBucketReducedOld = -1;
    1109           1 :             k = 0;
    1110           1 :             nOffFromBucketStart = 0;
    1111             :         }
    1112             : 
    1113           8 :         if (nOffInBucketReduced != l_nOffInBucketReducedOld)
    1114             :         {
    1115             :             std::map<int, Bucket>::const_iterator oIter =
    1116           1 :                 m_oMapBuckets.find(nBucket);
    1117           1 :             if (oIter == m_oMapBuckets.end())
    1118             :             {
    1119           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1120             :                          "Cannot read node " CPL_FRMT_GIB, id);
    1121           0 :                 continue;
    1122             :                 // FIXME ?
    1123             :             }
    1124           1 :             const Bucket *psBucket = &(oIter->second);
    1125           1 :             if (psBucket->u.panSectorSize == nullptr)
    1126             :             {
    1127           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1128             :                          "Cannot read node " CPL_FRMT_GIB, id);
    1129           0 :                 continue;
    1130             :                 // FIXME ?
    1131             :             }
    1132           1 :             const int nSectorSize = COMPRESS_SIZE_FROM_BYTE(
    1133           1 :                 psBucket->u.panSectorSize[nOffInBucketReduced]);
    1134             : 
    1135             :             /* If we stay in the same bucket, we can reuse the previously */
    1136             :             /* computed offset, instead of starting from bucket start */
    1137           1 :             for (; k < nOffInBucketReduced; k++)
    1138             :             {
    1139           0 :                 if (psBucket->u.panSectorSize[k])
    1140           0 :                     nOffFromBucketStart +=
    1141           0 :                         COMPRESS_SIZE_FROM_BYTE(psBucket->u.panSectorSize[k]);
    1142             :             }
    1143             : 
    1144           1 :             VSIFSeekL(m_fpNodes, psBucket->nOff + nOffFromBucketStart,
    1145             :                       SEEK_SET);
    1146           1 :             if (nSectorSize == SECTOR_SIZE)
    1147             :             {
    1148           0 :                 if (VSIFReadL(m_pabySector, 1, static_cast<size_t>(SECTOR_SIZE),
    1149           0 :                               m_fpNodes) != static_cast<size_t>(SECTOR_SIZE))
    1150             :                 {
    1151           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1152             :                              "Cannot read node " CPL_FRMT_GIB, id);
    1153           0 :                     continue;
    1154             :                     // FIXME ?
    1155             :                 }
    1156             :             }
    1157             :             else
    1158             :             {
    1159           1 :                 if (static_cast<int>(VSIFReadL(abyRawSector, 1, nSectorSize,
    1160           1 :                                                m_fpNodes)) != nSectorSize)
    1161             :                 {
    1162           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1163             :                              "Cannot read sector for node " CPL_FRMT_GIB, id);
    1164           0 :                     continue;
    1165             :                     // FIXME ?
    1166             :                 }
    1167           1 :                 abyRawSector[nSectorSize] = 0;
    1168             : 
    1169           1 :                 if (!DecompressSector(abyRawSector, nSectorSize, m_pabySector))
    1170             :                 {
    1171           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1172             :                              "Error while uncompressing sector for "
    1173             :                              "node " CPL_FRMT_GIB,
    1174             :                              id);
    1175           0 :                     continue;
    1176             :                     // FIXME ?
    1177             :                 }
    1178             :             }
    1179             : 
    1180           1 :             l_nBucketOld = nBucket;
    1181           1 :             l_nOffInBucketReducedOld = nOffInBucketReduced;
    1182             :         }
    1183             : 
    1184           8 :         m_panReqIds[j] = id;
    1185           8 :         memcpy(m_pasLonLatArray + j,
    1186           8 :                m_pabySector + nOffInBucketReducedRemainder * sizeof(LonLat),
    1187             :                sizeof(LonLat));
    1188             : 
    1189           8 :         if (m_pasLonLatArray[j].nLon || m_pasLonLatArray[j].nLat)
    1190           8 :             j++;
    1191             :     }
    1192           1 :     m_nReqIds = j;
    1193           1 : }
    1194             : 
    1195             : /************************************************************************/
    1196             : /*                    LookupNodesCustomNonCompressedCase()              */
    1197             : /************************************************************************/
    1198             : 
    1199          41 : void OGROSMDataSource::LookupNodesCustomNonCompressedCase()
    1200             : {
    1201          41 :     unsigned int j = 0;  // Used after for.
    1202             : 
    1203          41 :     int l_nBucketOld = -1;
    1204          41 :     const Bucket *psBucket = nullptr;
    1205             :     // To be glibc friendly, we will do reads aligned on 4096 byte offsets
    1206          41 :     const int knDISK_SECTOR_SIZE = 4096;
    1207          41 :     CPL_STATIC_ASSERT((knDISK_SECTOR_SIZE % SECTOR_SIZE) == 0);
    1208             :     GByte abyDiskSector[knDISK_SECTOR_SIZE];
    1209             :     // Offset in the nodes files for which abyDiskSector was read
    1210          41 :     GIntBig nOldOffset = -knDISK_SECTOR_SIZE - 1;
    1211             :     // Number of valid bytes in abyDiskSector
    1212          41 :     size_t nValidBytes = 0;
    1213          41 :     int k = 0;
    1214          41 :     int nSectorBase = 0;
    1215        1971 :     for (unsigned int i = 0; i < m_nReqIds; i++)
    1216             :     {
    1217        1930 :         const GIntBig id = m_panReqIds[i];
    1218        1930 :         const int nBucket = static_cast<int>(id / NODE_PER_BUCKET);
    1219        1930 :         const int nOffInBucket = static_cast<int>(id % NODE_PER_BUCKET);
    1220        1930 :         const int nOffInBucketReduced = nOffInBucket >> NODE_PER_SECTOR_SHIFT;
    1221        1930 :         const int nOffInBucketReducedRemainder =
    1222             :             nOffInBucket & ((1 << NODE_PER_SECTOR_SHIFT) - 1);
    1223             : 
    1224        1930 :         const int nBitmapIndex = nOffInBucketReduced / 8;
    1225        1930 :         const int nBitmapRemainder = nOffInBucketReduced % 8;
    1226             : 
    1227        1930 :         if (psBucket == nullptr || nBucket != l_nBucketOld)
    1228             :         {
    1229             :             std::map<int, Bucket>::const_iterator oIter =
    1230          40 :                 m_oMapBuckets.find(nBucket);
    1231          40 :             if (oIter == m_oMapBuckets.end())
    1232             :             {
    1233           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1234             :                          "Cannot read node " CPL_FRMT_GIB, id);
    1235           0 :                 continue;
    1236             :                 // FIXME ?
    1237             :             }
    1238          40 :             psBucket = &(oIter->second);
    1239          40 :             if (psBucket->u.pabyBitmap == nullptr)
    1240             :             {
    1241           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1242             :                          "Cannot read node " CPL_FRMT_GIB, id);
    1243           0 :                 continue;
    1244             :                 // FIXME ?
    1245             :             }
    1246          40 :             l_nBucketOld = nBucket;
    1247          40 :             nOldOffset = -knDISK_SECTOR_SIZE - 1;
    1248          40 :             k = 0;
    1249          40 :             nSectorBase = 0;
    1250             :         }
    1251             : 
    1252             :         /* If we stay in the same bucket, we can reuse the previously */
    1253             :         /* computed offset, instead of starting from bucket start */
    1254        1932 :         for (; k < nBitmapIndex; k++)
    1255             :         {
    1256           2 :             assert(psBucket->u.pabyBitmap);
    1257             :             // psBucket->u.pabyBitmap cannot be NULL
    1258             :             // coverity[var_deref_op]
    1259           2 :             nSectorBase += abyBitsCount[psBucket->u.pabyBitmap[k]];
    1260             :         }
    1261        1930 :         int nSector = nSectorBase;
    1262        1930 :         if (nBitmapRemainder)
    1263             :         {
    1264        1498 :             assert(psBucket->u.pabyBitmap);
    1265        1498 :             nSector += abyBitsCount[psBucket->u.pabyBitmap[nBitmapIndex] &
    1266        1498 :                                     ((1 << nBitmapRemainder) - 1)];
    1267             :         }
    1268             : 
    1269        1930 :         const GIntBig nNewOffset = psBucket->nOff + nSector * SECTOR_SIZE;
    1270        1930 :         if (nNewOffset - nOldOffset >= knDISK_SECTOR_SIZE)
    1271             :         {
    1272             :             // Align on 4096 boundary to be glibc caching friendly
    1273          43 :             const GIntBig nAlignedNewPos =
    1274             :                 nNewOffset & ~(static_cast<GIntBig>(knDISK_SECTOR_SIZE) - 1);
    1275          43 :             VSIFSeekL(m_fpNodes, nAlignedNewPos, SEEK_SET);
    1276             :             nValidBytes =
    1277          43 :                 VSIFReadL(abyDiskSector, 1, knDISK_SECTOR_SIZE, m_fpNodes);
    1278          43 :             nOldOffset = nAlignedNewPos;
    1279             :         }
    1280             : 
    1281        1930 :         const size_t nOffsetInDiskSector =
    1282        1930 :             static_cast<size_t>(nNewOffset - nOldOffset) +
    1283        1930 :             nOffInBucketReducedRemainder * sizeof(LonLat);
    1284        1930 :         if (nValidBytes < sizeof(LonLat) ||
    1285        1930 :             nOffsetInDiskSector > nValidBytes - sizeof(LonLat))
    1286             :         {
    1287           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1288             :                      "Cannot read node " CPL_FRMT_GIB, id);
    1289           0 :             continue;
    1290             :         }
    1291        1930 :         memcpy(&m_pasLonLatArray[j], abyDiskSector + nOffsetInDiskSector,
    1292             :                sizeof(LonLat));
    1293             : 
    1294        1930 :         m_panReqIds[j] = id;
    1295        1930 :         if (m_pasLonLatArray[j].nLon || m_pasLonLatArray[j].nLat)
    1296        1930 :             j++;
    1297             :     }
    1298          41 :     m_nReqIds = j;
    1299          41 : }
    1300             : 
    1301             : /************************************************************************/
    1302             : /*                            WriteVarInt()                             */
    1303             : /************************************************************************/
    1304             : 
    1305         406 : static void WriteVarInt(unsigned int nVal, std::vector<GByte> &abyData)
    1306             : {
    1307             :     while (true)
    1308             :     {
    1309         406 :         if ((nVal & (~0x7fU)) == 0)
    1310             :         {
    1311         406 :             abyData.push_back((GByte)nVal);
    1312         406 :             return;
    1313             :         }
    1314             : 
    1315           0 :         abyData.push_back(0x80 | (GByte)(nVal & 0x7f));
    1316           0 :         nVal >>= 7;
    1317             :     }
    1318             : }
    1319             : 
    1320             : /************************************************************************/
    1321             : /*                           WriteVarInt64()                            */
    1322             : /************************************************************************/
    1323             : 
    1324           0 : static void WriteVarInt64(GUIntBig nVal, std::vector<GByte> &abyData)
    1325             : {
    1326             :     while (true)
    1327             :     {
    1328           0 :         if ((((GUInt32)nVal) & (~0x7fU)) == 0)
    1329             :         {
    1330           0 :             abyData.push_back((GByte)nVal);
    1331           0 :             return;
    1332             :         }
    1333             : 
    1334           0 :         abyData.push_back(0x80 | (GByte)(nVal & 0x7f));
    1335           0 :         nVal >>= 7;
    1336             :     }
    1337             : }
    1338             : 
    1339             : /************************************************************************/
    1340             : /*                           WriteVarSInt64()                           */
    1341             : /************************************************************************/
    1342             : 
    1343        8234 : static void WriteVarSInt64(GIntBig nSVal, std::vector<GByte> &abyData)
    1344             : {
    1345        8234 :     GIntBig nVal = nSVal >= 0 ? nSVal << 1 : ((-1 - nSVal) << 1) + 1;
    1346             : 
    1347             :     while (true)
    1348             :     {
    1349       17058 :         if ((nVal & (~0x7f)) == 0)
    1350             :         {
    1351        8234 :             abyData.push_back((GByte)nVal);
    1352        8234 :             return;
    1353             :         }
    1354             : 
    1355        8824 :         abyData.push_back(0x80 | (GByte)(nVal & 0x7f));
    1356        8824 :         nVal >>= 7;
    1357             :     }
    1358             : }
    1359             : 
    1360             : /************************************************************************/
    1361             : /*                           WriteVarSInt64()                           */
    1362             : /************************************************************************/
    1363             : 
    1364          16 : static void WriteVarSInt64(GIntBig nSVal, GByte **ppabyData)
    1365             : {
    1366          16 :     GIntBig nVal = nSVal >= 0 ? nSVal << 1 : ((-1 - nSVal) << 1) + 1;
    1367             : 
    1368          16 :     GByte *pabyData = *ppabyData;
    1369             :     while (true)
    1370             :     {
    1371          45 :         if ((nVal & (~0x7f)) == 0)
    1372             :         {
    1373          16 :             *pabyData = (GByte)nVal;
    1374          16 :             *ppabyData = pabyData + 1;
    1375          16 :             return;
    1376             :         }
    1377             : 
    1378          29 :         *pabyData = 0x80 | (GByte)(nVal & 0x7f);
    1379          29 :         nVal >>= 7;
    1380          29 :         pabyData++;
    1381             :     }
    1382             : }
    1383             : 
    1384             : /************************************************************************/
    1385             : /*                             CompressWay()                            */
    1386             : /************************************************************************/
    1387             : 
    1388         591 : void OGROSMDataSource::CompressWay(bool bIsArea, unsigned int nTags,
    1389             :                                    IndexedKVP *pasTags, int nPoints,
    1390             :                                    LonLat *pasLonLatPairs, OSMInfo *psInfo,
    1391             :                                    std::vector<GByte> &abyCompressedWay)
    1392             : {
    1393         591 :     abyCompressedWay.clear();
    1394         591 :     abyCompressedWay.push_back((bIsArea) ? 1 : 0);
    1395         591 :     CPLAssert(nTags <= MAX_COUNT_FOR_TAGS_IN_WAY);
    1396         591 :     abyCompressedWay.push_back(static_cast<GByte>(nTags));
    1397             : 
    1398         797 :     for (unsigned int iTag = 0; iTag < nTags; iTag++)
    1399             :     {
    1400         206 :         if (pasTags[iTag].bKIsIndex)
    1401             :         {
    1402         204 :             WriteVarInt(pasTags[iTag].uKey.nKeyIndex, abyCompressedWay);
    1403             :         }
    1404             :         else
    1405             :         {
    1406           2 :             const char *pszK = (const char *)pabyNonRedundantKeys +
    1407           2 :                                pasTags[iTag].uKey.nOffsetInpabyNonRedundantKeys;
    1408             : 
    1409           2 :             abyCompressedWay.push_back(0);
    1410             : 
    1411             :             abyCompressedWay.insert(
    1412           0 :                 abyCompressedWay.end(), reinterpret_cast<const GByte *>(pszK),
    1413           2 :                 reinterpret_cast<const GByte *>(pszK) + strlen(pszK) + 1);
    1414             :         }
    1415             : 
    1416         206 :         if (pasTags[iTag].bVIsIndex)
    1417             :         {
    1418         202 :             WriteVarInt(pasTags[iTag].uVal.nValueIndex, abyCompressedWay);
    1419             :         }
    1420             :         else
    1421             :         {
    1422           4 :             const char *pszV =
    1423           4 :                 (const char *)pabyNonRedundantValues +
    1424           4 :                 pasTags[iTag].uVal.nOffsetInpabyNonRedundantValues;
    1425             : 
    1426           4 :             if (pasTags[iTag].bKIsIndex)
    1427           2 :                 abyCompressedWay.push_back(0);
    1428             : 
    1429             :             abyCompressedWay.insert(
    1430           0 :                 abyCompressedWay.end(), reinterpret_cast<const GByte *>(pszV),
    1431           4 :                 reinterpret_cast<const GByte *>(pszV) + strlen(pszV) + 1);
    1432             :         }
    1433             :     }
    1434             : 
    1435         591 :     if (m_bNeedsToSaveWayInfo)
    1436             :     {
    1437           0 :         if (psInfo != nullptr)
    1438             :         {
    1439           0 :             abyCompressedWay.push_back(1);
    1440           0 :             WriteVarInt64(psInfo->ts.nTimeStamp, abyCompressedWay);
    1441           0 :             WriteVarInt64(psInfo->nChangeset, abyCompressedWay);
    1442           0 :             WriteVarInt(psInfo->nVersion, abyCompressedWay);
    1443           0 :             WriteVarInt(psInfo->nUID, abyCompressedWay);
    1444             :             // FIXME : do something with pszUserSID
    1445             :         }
    1446             :         else
    1447             :         {
    1448           0 :             abyCompressedWay.push_back(0);
    1449             :         }
    1450             :     }
    1451             : 
    1452             :     abyCompressedWay.insert(
    1453        1182 :         abyCompressedWay.end(),
    1454             :         reinterpret_cast<const GByte *>(&(pasLonLatPairs[0])),
    1455         591 :         reinterpret_cast<const GByte *>(&(pasLonLatPairs[0])) + sizeof(LonLat));
    1456        4708 :     for (int i = 1; i < nPoints; i++)
    1457             :     {
    1458        4117 :         GIntBig nDiff64 = (GIntBig)pasLonLatPairs[i].nLon -
    1459        4117 :                           (GIntBig)pasLonLatPairs[i - 1].nLon;
    1460        4117 :         WriteVarSInt64(nDiff64, abyCompressedWay);
    1461             : 
    1462        4117 :         nDiff64 = pasLonLatPairs[i].nLat - pasLonLatPairs[i - 1].nLat;
    1463        4117 :         WriteVarSInt64(nDiff64, abyCompressedWay);
    1464             :     }
    1465         591 : }
    1466             : 
    1467             : /************************************************************************/
    1468             : /*                             UncompressWay()                          */
    1469             : /************************************************************************/
    1470             : 
    1471         256 : void OGROSMDataSource::UncompressWay(int nBytes, const GByte *pabyCompressedWay,
    1472             :                                      bool *pbIsArea,
    1473             :                                      std::vector<LonLat> &asCoords,
    1474             :                                      unsigned int *pnTags, OSMTag *pasTags,
    1475             :                                      OSMInfo *psInfo)
    1476             : {
    1477         256 :     asCoords.clear();
    1478         256 :     const GByte *pabyPtr = pabyCompressedWay;
    1479         256 :     if (pbIsArea)
    1480          33 :         *pbIsArea = (*pabyPtr == 1) ? true : false;
    1481         256 :     pabyPtr++;
    1482         256 :     unsigned int nTags = *pabyPtr;
    1483         256 :     pabyPtr++;
    1484             : 
    1485         256 :     if (pnTags)
    1486         162 :         *pnTags = nTags;
    1487             : 
    1488             :     // TODO: Some additional safety checks.
    1489         482 :     for (unsigned int iTag = 0; iTag < nTags; iTag++)
    1490             :     {
    1491         226 :         const int nK = ReadVarInt32(&pabyPtr);
    1492         226 :         const GByte *pszK = nullptr;
    1493         226 :         if (nK == 0)
    1494             :         {
    1495           3 :             pszK = pabyPtr;
    1496          21 :             while (*pabyPtr != '\0')
    1497          18 :                 pabyPtr++;
    1498           3 :             pabyPtr++;
    1499             :         }
    1500             : 
    1501         226 :         const int nV = nK == 0 ? 0 : ReadVarInt32(&pabyPtr);
    1502         226 :         const GByte *pszV = nullptr;
    1503         226 :         if (nV == 0)
    1504             :         {
    1505           6 :             pszV = pabyPtr;
    1506          58 :             while (*pabyPtr != '\0')
    1507          52 :                 pabyPtr++;
    1508           6 :             pabyPtr++;
    1509             :         }
    1510             : 
    1511         226 :         if (pasTags)
    1512             :         {
    1513         206 :             CPLAssert(nK >= 0 && nK < (int)m_asKeys.size());
    1514         206 :             pasTags[iTag].pszK =
    1515         206 :                 nK ? m_asKeys[nK]->pszK : reinterpret_cast<const char *>(pszK);
    1516             : 
    1517         206 :             CPLAssert(nK == 0 ||
    1518             :                       (nV >= 0 && nV < (int)m_asKeys[nK]->asValues.size()));
    1519         206 :             pasTags[iTag].pszV =
    1520         206 :                 nV ? m_asKeys[nK]->asValues[nV] : (const char *)pszV;
    1521             :         }
    1522             :     }
    1523             : 
    1524         256 :     if (m_bNeedsToSaveWayInfo)
    1525             :     {
    1526           0 :         if (*pabyPtr)
    1527             :         {
    1528           0 :             pabyPtr++;
    1529             : 
    1530             :             OSMInfo sInfo;
    1531           0 :             if (psInfo == nullptr)
    1532           0 :                 psInfo = &sInfo;
    1533             : 
    1534           0 :             psInfo->ts.nTimeStamp = ReadVarInt64(&pabyPtr);
    1535           0 :             psInfo->nChangeset = ReadVarInt64(&pabyPtr);
    1536           0 :             psInfo->nVersion = ReadVarInt32(&pabyPtr);
    1537           0 :             psInfo->nUID = ReadVarInt32(&pabyPtr);
    1538             : 
    1539           0 :             psInfo->bTimeStampIsStr = false;
    1540           0 :             psInfo->pszUserSID = "";  // FIXME
    1541             :         }
    1542             :         else
    1543           0 :             pabyPtr++;
    1544             :     }
    1545             : 
    1546             :     LonLat lonLat;
    1547         256 :     memcpy(&lonLat.nLon, pabyPtr, sizeof(int));
    1548         256 :     memcpy(&lonLat.nLat, pabyPtr + sizeof(int), sizeof(int));
    1549         256 :     asCoords.emplace_back(lonLat);
    1550         256 :     pabyPtr += 2 * sizeof(int);
    1551        1131 :     do
    1552             :     {
    1553        1387 :         lonLat.nLon = (int)(lonLat.nLon + ReadVarSInt64(&pabyPtr));
    1554        1387 :         lonLat.nLat = (int)(lonLat.nLat + ReadVarSInt64(&pabyPtr));
    1555        1387 :         asCoords.emplace_back(lonLat);
    1556        1387 :     } while (pabyPtr < pabyCompressedWay + nBytes);
    1557         256 : }
    1558             : 
    1559             : /************************************************************************/
    1560             : /*                              IndexWay()                              */
    1561             : /************************************************************************/
    1562             : 
    1563         594 : void OGROSMDataSource::IndexWay(GIntBig nWayID, bool bIsArea,
    1564             :                                 unsigned int nTags, IndexedKVP *pasTags,
    1565             :                                 LonLat *pasLonLatPairs, int nPairs,
    1566             :                                 OSMInfo *psInfo)
    1567             : {
    1568         594 :     if (!m_bIndexWays)
    1569           3 :         return;
    1570             : 
    1571         591 :     sqlite3_bind_int64(m_hInsertWayStmt, 1, nWayID);
    1572             : 
    1573         591 :     const unsigned nTagsClamped = std::min(nTags, MAX_COUNT_FOR_TAGS_IN_WAY);
    1574         591 :     if (nTagsClamped < nTags)
    1575             :     {
    1576           0 :         CPLDebug("OSM",
    1577             :                  "Too many tags for way " CPL_FRMT_GIB ": %u. "
    1578             :                  "Clamping to %u",
    1579             :                  nWayID, nTags, nTagsClamped);
    1580             :     }
    1581         591 :     CompressWay(bIsArea, nTagsClamped, pasTags, nPairs, pasLonLatPairs, psInfo,
    1582         591 :                 m_abyWayBuffer);
    1583         591 :     sqlite3_bind_blob(m_hInsertWayStmt, 2, m_abyWayBuffer.data(),
    1584         591 :                       static_cast<int>(m_abyWayBuffer.size()), SQLITE_STATIC);
    1585             : 
    1586         591 :     int rc = sqlite3_step(m_hInsertWayStmt);
    1587         591 :     sqlite3_reset(m_hInsertWayStmt);
    1588         591 :     if (!(rc == SQLITE_OK || rc == SQLITE_DONE))
    1589             :     {
    1590           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1591             :                  "Failed inserting way " CPL_FRMT_GIB ": %s", nWayID,
    1592             :                  sqlite3_errmsg(m_hDB));
    1593             :     }
    1594             : }
    1595             : 
    1596             : /************************************************************************/
    1597             : /*                              FindNode()                              */
    1598             : /************************************************************************/
    1599             : 
    1600          26 : int OGROSMDataSource::FindNode(GIntBig nID)
    1601             : {
    1602          26 :     if (m_nReqIds == 0)
    1603          26 :         return -1;
    1604           0 :     int iFirst = 0;
    1605           0 :     int iLast = m_nReqIds - 1;
    1606           0 :     while (iFirst < iLast)
    1607             :     {
    1608           0 :         int iMid = (iFirst + iLast) / 2;
    1609           0 :         if (nID > m_panReqIds[iMid])
    1610           0 :             iFirst = iMid + 1;
    1611             :         else
    1612           0 :             iLast = iMid;
    1613             :     }
    1614           0 :     if (iFirst == iLast && nID == m_panReqIds[iFirst])
    1615           0 :         return iFirst;
    1616           0 :     return -1;
    1617             : }
    1618             : 
    1619             : /************************************************************************/
    1620             : /*                         ProcessWaysBatch()                           */
    1621             : /************************************************************************/
    1622             : 
    1623          29 : void OGROSMDataSource::ProcessWaysBatch()
    1624             : {
    1625          29 :     if (m_nWayFeaturePairs == 0)
    1626           0 :         return;
    1627             : 
    1628             :     // printf("nodes = %d, features = %d\n", nUnsortedReqIds, nWayFeaturePairs);
    1629          29 :     LookupNodes();
    1630             : 
    1631         654 :     for (int iPair = 0; iPair < m_nWayFeaturePairs; iPair++)
    1632             :     {
    1633         625 :         WayFeaturePair *psWayFeaturePairs = &m_pasWayFeaturePairs[iPair];
    1634             : 
    1635         625 :         const bool bIsArea = psWayFeaturePairs->bIsArea;
    1636         625 :         m_asLonLatCache.clear();
    1637             : 
    1638             : #ifdef ENABLE_NODE_LOOKUP_BY_HASHING
    1639         625 :         if (m_bHashedIndexValid)
    1640             :         {
    1641        5265 :             for (unsigned int i = 0; i < psWayFeaturePairs->nRefs; i++)
    1642             :             {
    1643        4647 :                 int nIndInHashArray = static_cast<int>(
    1644        4647 :                     HASH_ID_FUNC(psWayFeaturePairs->panNodeRefs[i]) %
    1645             :                     HASHED_INDEXES_ARRAY_SIZE);
    1646        4647 :                 int nIdx = m_panHashedIndexes[nIndInHashArray];
    1647        4647 :                 if (nIdx < -1)
    1648             :                 {
    1649           0 :                     int iBucket = -nIdx - 2;
    1650             :                     while (true)
    1651             :                     {
    1652           0 :                         nIdx = m_psCollisionBuckets[iBucket].nInd;
    1653           0 :                         if (m_panReqIds[nIdx] ==
    1654           0 :                             psWayFeaturePairs->panNodeRefs[i])
    1655           0 :                             break;
    1656           0 :                         iBucket = m_psCollisionBuckets[iBucket].nNext;
    1657           0 :                         if (iBucket < 0)
    1658             :                         {
    1659           0 :                             nIdx = -1;
    1660           0 :                             break;
    1661             :                         }
    1662             :                     }
    1663             :                 }
    1664        4647 :                 else if (nIdx >= 0 &&
    1665        4527 :                          m_panReqIds[nIdx] != psWayFeaturePairs->panNodeRefs[i])
    1666           0 :                     nIdx = -1;
    1667             : 
    1668        4647 :                 if (nIdx >= 0)
    1669             :                 {
    1670        4527 :                     m_asLonLatCache.push_back(m_pasLonLatArray[nIdx]);
    1671             :                 }
    1672             :             }
    1673             :         }
    1674             :         else
    1675             : #endif  // ENABLE_NODE_LOOKUP_BY_HASHING
    1676             :         {
    1677           7 :             int nIdx = -1;
    1678          33 :             for (unsigned int i = 0; i < psWayFeaturePairs->nRefs; i++)
    1679             :             {
    1680          26 :                 if (nIdx >= 0 && psWayFeaturePairs->panNodeRefs[i] ==
    1681           0 :                                      psWayFeaturePairs->panNodeRefs[i - 1] + 1)
    1682             :                 {
    1683           0 :                     if (nIdx + 1 < (int)m_nReqIds &&
    1684           0 :                         m_panReqIds[nIdx + 1] ==
    1685           0 :                             psWayFeaturePairs->panNodeRefs[i])
    1686           0 :                         nIdx++;
    1687             :                     else
    1688           0 :                         nIdx = -1;
    1689             :                 }
    1690             :                 else
    1691          26 :                     nIdx = FindNode(psWayFeaturePairs->panNodeRefs[i]);
    1692          26 :                 if (nIdx >= 0)
    1693             :                 {
    1694           0 :                     m_asLonLatCache.push_back(m_pasLonLatArray[nIdx]);
    1695             :                 }
    1696             :             }
    1697             :         }
    1698             : 
    1699         625 :         if (!m_asLonLatCache.empty() && bIsArea)
    1700             :         {
    1701         190 :             m_asLonLatCache.push_back(m_asLonLatCache[0]);
    1702             :         }
    1703             : 
    1704         625 :         if (m_asLonLatCache.size() < 2)
    1705             :         {
    1706          31 :             CPLDebug("OSM",
    1707             :                      "Way " CPL_FRMT_GIB
    1708             :                      " with %d nodes that could be found. Discarding it",
    1709             :                      psWayFeaturePairs->nWayID,
    1710          31 :                      static_cast<int>(m_asLonLatCache.size()));
    1711          31 :             delete psWayFeaturePairs->poFeature;
    1712          31 :             psWayFeaturePairs->poFeature = nullptr;
    1713          31 :             psWayFeaturePairs->bIsArea = false;
    1714         256 :             continue;
    1715             :         }
    1716             : 
    1717         594 :         if (bIsArea && m_papoLayers[IDX_LYR_MULTIPOLYGONS]->IsUserInterested())
    1718             :         {
    1719         183 :             IndexWay(psWayFeaturePairs->nWayID, /*bIsArea = */ true,
    1720             :                      psWayFeaturePairs->nTags, psWayFeaturePairs->pasTags,
    1721             :                      m_asLonLatCache.data(),
    1722         183 :                      static_cast<int>(m_asLonLatCache.size()),
    1723             :                      &psWayFeaturePairs->sInfo);
    1724             :         }
    1725             :         else
    1726         411 :             IndexWay(psWayFeaturePairs->nWayID, bIsArea, 0, nullptr,
    1727             :                      m_asLonLatCache.data(),
    1728         411 :                      static_cast<int>(m_asLonLatCache.size()), nullptr);
    1729             : 
    1730         594 :         if (psWayFeaturePairs->poFeature == nullptr)
    1731             :         {
    1732         225 :             continue;
    1733             :         }
    1734             : 
    1735         369 :         OGRLineString *poLS = new OGRLineString();
    1736         369 :         OGRGeometry *poGeom = poLS;
    1737             : 
    1738         369 :         const int nPoints = static_cast<int>(m_asLonLatCache.size());
    1739         369 :         poLS->setNumPoints(nPoints);
    1740        3589 :         for (int i = 0; i < nPoints; i++)
    1741             :         {
    1742        3220 :             poLS->setPoint(i, INT_TO_DBL(m_asLonLatCache[i].nLon),
    1743        3220 :                            INT_TO_DBL(m_asLonLatCache[i].nLat));
    1744             :         }
    1745             : 
    1746         369 :         psWayFeaturePairs->poFeature->SetGeometryDirectly(poGeom);
    1747             : 
    1748         369 :         if (m_asLonLatCache.size() != psWayFeaturePairs->nRefs)
    1749          18 :             CPLDebug(
    1750             :                 "OSM",
    1751             :                 "For way " CPL_FRMT_GIB ", got only %d nodes instead of %d",
    1752             :                 psWayFeaturePairs->nWayID, nPoints, psWayFeaturePairs->nRefs);
    1753             : 
    1754         369 :         int bFilteredOut = FALSE;
    1755         369 :         if (!m_papoLayers[IDX_LYR_LINES]->AddFeature(
    1756             :                 psWayFeaturePairs->poFeature,
    1757         369 :                 psWayFeaturePairs->bAttrFilterAlreadyEvaluated, &bFilteredOut,
    1758         369 :                 !m_bFeatureAdded))
    1759           0 :             m_bStopParsing = true;
    1760         369 :         else if (!bFilteredOut)
    1761         368 :             m_bFeatureAdded = true;
    1762             :     }
    1763             : 
    1764          29 :     if (m_papoLayers[IDX_LYR_MULTIPOLYGONS]->IsUserInterested())
    1765             :     {
    1766         630 :         for (int iPair = 0; iPair < m_nWayFeaturePairs; iPair++)
    1767             :         {
    1768         606 :             WayFeaturePair *psWayFeaturePairs = &m_pasWayFeaturePairs[iPair];
    1769             : 
    1770         606 :             if (psWayFeaturePairs->bIsArea &&
    1771         183 :                 (psWayFeaturePairs->nTags || m_bReportAllWays))
    1772             :             {
    1773         162 :                 sqlite3_bind_int64(m_hInsertPolygonsStandaloneStmt, 1,
    1774             :                                    psWayFeaturePairs->nWayID);
    1775             : 
    1776         162 :                 int rc = sqlite3_step(m_hInsertPolygonsStandaloneStmt);
    1777         162 :                 sqlite3_reset(m_hInsertPolygonsStandaloneStmt);
    1778         162 :                 if (!(rc == SQLITE_OK || rc == SQLITE_DONE))
    1779             :                 {
    1780           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1781             :                              "Failed inserting into "
    1782             :                              "polygons_standalone " CPL_FRMT_GIB ": %s",
    1783             :                              psWayFeaturePairs->nWayID, sqlite3_errmsg(m_hDB));
    1784             :                 }
    1785             :             }
    1786             :         }
    1787             :     }
    1788             : 
    1789          29 :     m_nWayFeaturePairs = 0;
    1790          29 :     m_nUnsortedReqIds = 0;
    1791             : 
    1792          29 :     m_nAccumulatedTags = 0;
    1793          29 :     nNonRedundantKeysLen = 0;
    1794          29 :     nNonRedundantValuesLen = 0;
    1795             : }
    1796             : 
    1797             : /************************************************************************/
    1798             : /*                      IsClosedWayTaggedAsPolygon()                    */
    1799             : /************************************************************************/
    1800             : 
    1801         255 : bool OGROSMDataSource::IsClosedWayTaggedAsPolygon(unsigned int nTags,
    1802             :                                                   const OSMTag *pasTags)
    1803             : {
    1804         255 :     bool bIsArea = false;
    1805         255 :     const int nSizeArea = 4;
    1806             :     const int nStrnlenK =
    1807         255 :         std::max(nSizeArea, m_nMaxSizeKeysInSetClosedWaysArePolygons) + 1;
    1808         255 :     std::string oTmpStr;
    1809         255 :     oTmpStr.reserve(m_nMaxSizeKeysInSetClosedWaysArePolygons);
    1810         468 :     for (unsigned int i = 0; i < nTags; i++)
    1811             :     {
    1812         299 :         const char *pszK = pasTags[i].pszK;
    1813         299 :         const int nKLen = static_cast<int>(CPLStrnlen(pszK, nStrnlenK));
    1814         299 :         if (nKLen > m_nMaxSizeKeysInSetClosedWaysArePolygons)
    1815           0 :             continue;
    1816             : 
    1817         299 :         if (nKLen == nSizeArea && strcmp(pszK, "area") == 0)
    1818             :         {
    1819          86 :             const char *pszV = pasTags[i].pszV;
    1820          86 :             if (strcmp(pszV, "yes") == 0)
    1821             :             {
    1822          86 :                 bIsArea = true;
    1823             :                 // final true. We can't have several area tags...
    1824          86 :                 break;
    1825             :             }
    1826           0 :             else if (strcmp(pszV, "no") == 0)
    1827             :             {
    1828           0 :                 bIsArea = false;
    1829           0 :                 break;
    1830             :             }
    1831             :         }
    1832         213 :         if (bIsArea)
    1833          40 :             continue;
    1834             : 
    1835         173 :         if (nKLen >= m_nMinSizeKeysInSetClosedWaysArePolygons)
    1836             :         {
    1837         173 :             oTmpStr.assign(pszK, nKLen);
    1838         173 :             if (aoSetClosedWaysArePolygons.find(oTmpStr) !=
    1839         346 :                 aoSetClosedWaysArePolygons.end())
    1840             :             {
    1841         118 :                 bIsArea = true;
    1842         118 :                 continue;
    1843             :             }
    1844             :         }
    1845             : 
    1846          55 :         const char *pszV = pasTags[i].pszV;
    1847          55 :         const int nVLen = static_cast<int>(CPLStrnlen(pszV, nStrnlenK));
    1848          55 :         if (nKLen + 1 + nVLen >= m_nMinSizeKeysInSetClosedWaysArePolygons &&
    1849          55 :             nKLen + 1 + nVLen <= m_nMaxSizeKeysInSetClosedWaysArePolygons)
    1850             :         {
    1851          55 :             oTmpStr.assign(pszK, nKLen);
    1852          55 :             oTmpStr.append(1, '=');
    1853          55 :             oTmpStr.append(pszV, nVLen);
    1854          55 :             if (aoSetClosedWaysArePolygons.find(oTmpStr) !=
    1855         110 :                 aoSetClosedWaysArePolygons.end())
    1856             :             {
    1857           0 :                 bIsArea = true;
    1858           0 :                 continue;
    1859             :             }
    1860             :         }
    1861             :     }
    1862         510 :     return bIsArea;
    1863             : }
    1864             : 
    1865             : /************************************************************************/
    1866             : /*                              NotifyWay()                             */
    1867             : /************************************************************************/
    1868             : 
    1869         717 : void OGROSMDataSource::NotifyWay(OSMWay *psWay)
    1870             : {
    1871         717 :     m_nWaysProcessed++;
    1872         717 :     if (m_nWaysProcessed % 10000 == 0)
    1873             :     {
    1874           0 :         CPLDebug("OSM", "Ways processed : %d", m_nWaysProcessed);
    1875             : #ifdef DEBUG_MEM_USAGE
    1876             :         CPLDebug("OSM", "GetMaxTotalAllocs() = " CPL_FRMT_GUIB,
    1877             :                  static_cast<GUIntBig>(GetMaxTotalAllocs()));
    1878             : #endif
    1879             :     }
    1880             : 
    1881         717 :     if (!m_bUsePointsIndex)
    1882          48 :         return;
    1883             : 
    1884             :     // printf("way %d : %d nodes\n", (int)psWay->nID, (int)psWay->nRefs);
    1885             : 
    1886         669 :     if (psWay->nRefs < 2)
    1887             :     {
    1888          27 :         CPLDebug("OSM", "Way " CPL_FRMT_GIB " with %d nodes. Discarding it",
    1889             :                  psWay->nID, psWay->nRefs);
    1890          27 :         return;
    1891             :     }
    1892             : 
    1893             :     /* Is a closed way a polygon ? */
    1894         642 :     bool bIsArea = false;
    1895         642 :     if (psWay->panNodeRefs[0] == psWay->panNodeRefs[psWay->nRefs - 1])
    1896             :     {
    1897         255 :         bIsArea = IsClosedWayTaggedAsPolygon(psWay->nTags, psWay->pasTags);
    1898             :     }
    1899             : 
    1900         642 :     bool bInterestingTag = m_bReportAllWays;
    1901         642 :     if (!bIsArea && !m_bReportAllWays)
    1902             :     {
    1903         440 :         for (unsigned int i = 0; i < psWay->nTags; i++)
    1904             :         {
    1905         413 :             const char *pszK = psWay->pasTags[i].pszK;
    1906         413 :             if (m_papoLayers[IDX_LYR_LINES]->IsSignificantKey(pszK))
    1907             :             {
    1908         413 :                 bInterestingTag = true;
    1909         413 :                 break;
    1910             :             }
    1911             :         }
    1912             :     }
    1913             : 
    1914         642 :     OGRFeature *poFeature = nullptr;
    1915         642 :     bool bAttrFilterAlreadyEvaluated = false;
    1916         642 :     if (!bIsArea && m_papoLayers[IDX_LYR_LINES]->IsUserInterested() &&
    1917             :         bInterestingTag)
    1918             :     {
    1919         395 :         poFeature = new OGRFeature(m_papoLayers[IDX_LYR_LINES]->GetLayerDefn());
    1920             : 
    1921         395 :         m_papoLayers[IDX_LYR_LINES]->SetFieldsFromTags(
    1922             :             poFeature, psWay->nID, false, psWay->nTags, psWay->pasTags,
    1923             :             &psWay->sInfo);
    1924             : 
    1925             :         // Optimization: if we have an attribute filter, that does not require
    1926             :         // geometry, and if we don't need to index ways, then we can just
    1927             :         // evaluate the attribute filter without the geometry.
    1928         395 :         if (m_papoLayers[IDX_LYR_LINES]->HasAttributeFilter() &&
    1929           9 :             !m_papoLayers[IDX_LYR_LINES]
    1930         404 :                  ->AttributeFilterEvaluationNeedsGeometry() &&
    1931           9 :             !m_bIndexWays)
    1932             :         {
    1933           6 :             if (!m_papoLayers[IDX_LYR_LINES]->EvaluateAttributeFilter(
    1934             :                     poFeature))
    1935             :             {
    1936           5 :                 delete poFeature;
    1937           5 :                 return;
    1938             :             }
    1939           1 :             bAttrFilterAlreadyEvaluated = true;
    1940             :         }
    1941             :     }
    1942         247 :     else if (!m_bIndexWays)
    1943             :     {
    1944          12 :         return;
    1945             :     }
    1946             : 
    1947         625 :     if (m_nUnsortedReqIds + psWay->nRefs >
    1948         625 :             static_cast<unsigned int>(MAX_ACCUMULATED_NODES) ||
    1949         625 :         m_nWayFeaturePairs == MAX_DELAYED_FEATURES ||
    1950         625 :         m_nAccumulatedTags + psWay->nTags >
    1951         625 :             static_cast<unsigned int>(MAX_ACCUMULATED_TAGS) ||
    1952         625 :         nNonRedundantKeysLen + 1024 > MAX_NON_REDUNDANT_KEYS ||
    1953         625 :         nNonRedundantValuesLen + 1024 > MAX_NON_REDUNDANT_VALUES)
    1954             :     {
    1955           0 :         ProcessWaysBatch();
    1956             :     }
    1957             : 
    1958         625 :     WayFeaturePair *psWayFeaturePairs =
    1959         625 :         &m_pasWayFeaturePairs[m_nWayFeaturePairs];
    1960             : 
    1961         625 :     psWayFeaturePairs->nWayID = psWay->nID;
    1962         625 :     psWayFeaturePairs->nRefs = psWay->nRefs - (bIsArea ? 1 : 0);
    1963         625 :     psWayFeaturePairs->panNodeRefs = m_panUnsortedReqIds + m_nUnsortedReqIds;
    1964         625 :     psWayFeaturePairs->poFeature = poFeature;
    1965         625 :     psWayFeaturePairs->bIsArea = bIsArea;
    1966         625 :     psWayFeaturePairs->bAttrFilterAlreadyEvaluated =
    1967             :         bAttrFilterAlreadyEvaluated;
    1968             : 
    1969         625 :     if (bIsArea && m_papoLayers[IDX_LYR_MULTIPOLYGONS]->IsUserInterested())
    1970             :     {
    1971         186 :         unsigned int nTagCount = 0;
    1972             : 
    1973         186 :         if (m_bNeedsToSaveWayInfo)
    1974             :         {
    1975           0 :             if (!psWay->sInfo.bTimeStampIsStr)
    1976           0 :                 psWayFeaturePairs->sInfo.ts.nTimeStamp =
    1977           0 :                     psWay->sInfo.ts.nTimeStamp;
    1978             :             else
    1979             :             {
    1980             :                 OGRField sField;
    1981           0 :                 if (OGRParseXMLDateTime(psWay->sInfo.ts.pszTimeStamp, &sField))
    1982             :                 {
    1983             :                     struct tm brokendown;
    1984           0 :                     memset(&brokendown, 0, sizeof(brokendown));
    1985           0 :                     brokendown.tm_year = sField.Date.Year - 1900;
    1986           0 :                     brokendown.tm_mon = sField.Date.Month - 1;
    1987           0 :                     brokendown.tm_mday = sField.Date.Day;
    1988           0 :                     brokendown.tm_hour = sField.Date.Hour;
    1989           0 :                     brokendown.tm_min = sField.Date.Minute;
    1990           0 :                     brokendown.tm_sec = (int)(sField.Date.Second + .5);
    1991           0 :                     psWayFeaturePairs->sInfo.ts.nTimeStamp =
    1992           0 :                         CPLYMDHMSToUnixTime(&brokendown);
    1993             :                 }
    1994             :                 else
    1995           0 :                     psWayFeaturePairs->sInfo.ts.nTimeStamp = 0;
    1996             :             }
    1997           0 :             psWayFeaturePairs->sInfo.nChangeset = psWay->sInfo.nChangeset;
    1998           0 :             psWayFeaturePairs->sInfo.nVersion = psWay->sInfo.nVersion;
    1999           0 :             psWayFeaturePairs->sInfo.nUID = psWay->sInfo.nUID;
    2000           0 :             psWayFeaturePairs->sInfo.bTimeStampIsStr = false;
    2001           0 :             psWayFeaturePairs->sInfo.pszUserSID = "";  // FIXME
    2002             :         }
    2003             :         else
    2004             :         {
    2005         186 :             psWayFeaturePairs->sInfo.ts.nTimeStamp = 0;
    2006         186 :             psWayFeaturePairs->sInfo.nChangeset = 0;
    2007         186 :             psWayFeaturePairs->sInfo.nVersion = 0;
    2008         186 :             psWayFeaturePairs->sInfo.nUID = 0;
    2009         186 :             psWayFeaturePairs->sInfo.bTimeStampIsStr = false;
    2010         186 :             psWayFeaturePairs->sInfo.pszUserSID = "";
    2011             :         }
    2012             : 
    2013         186 :         psWayFeaturePairs->pasTags = m_pasAccumulatedTags + m_nAccumulatedTags;
    2014             : 
    2015         466 :         for (unsigned int iTag = 0; iTag < psWay->nTags; iTag++)
    2016             :         {
    2017         280 :             const char *pszK = psWay->pasTags[iTag].pszK;
    2018         280 :             const char *pszV = psWay->pasTags[iTag].pszV;
    2019             : 
    2020         280 :             if (std::any_of(begin(m_ignoredKeys), end(m_ignoredKeys),
    2021        1530 :                             [pszK](const char *pszIgnoredKey)
    2022        1530 :                             { return strcmp(pszK, pszIgnoredKey) == 0; }))
    2023             :             {
    2024          72 :                 continue;
    2025             :             }
    2026             : 
    2027         208 :             auto oIterK = m_aoMapIndexedKeys.find(pszK);
    2028         208 :             KeyDesc *psKD = nullptr;
    2029         208 :             if (oIterK == m_aoMapIndexedKeys.end())
    2030             :             {
    2031          74 :                 if (m_asKeys.size() >= 1 + MAX_INDEXED_KEYS)
    2032             :                 {
    2033           2 :                     if (m_asKeys.size() == 1 + MAX_INDEXED_KEYS)
    2034             :                     {
    2035           1 :                         CPLDebug("OSM", "More than %d different keys found",
    2036             :                                  MAX_INDEXED_KEYS);
    2037             :                         // To avoid next warnings.
    2038           1 :                         m_asKeys.push_back(nullptr);
    2039             :                     }
    2040             : 
    2041           2 :                     const int nLenK = static_cast<int>(strlen(pszK)) + 1;
    2042           2 :                     if (nNonRedundantKeysLen + nLenK > MAX_NON_REDUNDANT_KEYS)
    2043             :                     {
    2044           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    2045             :                                  "Too many/too long keys found");
    2046           0 :                         continue;
    2047             :                     }
    2048           2 :                     memcpy(pabyNonRedundantKeys + nNonRedundantKeysLen, pszK,
    2049             :                            nLenK);
    2050           2 :                     m_pasAccumulatedTags[m_nAccumulatedTags].bKIsIndex = FALSE;
    2051           2 :                     m_pasAccumulatedTags[m_nAccumulatedTags]
    2052           2 :                         .uKey.nOffsetInpabyNonRedundantKeys =
    2053           2 :                         nNonRedundantKeysLen;
    2054           2 :                     nNonRedundantKeysLen += nLenK;
    2055             :                 }
    2056             :                 else
    2057             :                 {
    2058          72 :                     psKD = new KeyDesc();
    2059          72 :                     psKD->pszK = CPLStrdup(pszK);
    2060          72 :                     psKD->nKeyIndex = static_cast<int>(m_asKeys.size());
    2061          72 :                     psKD->nOccurrences = 0;
    2062          72 :                     psKD->asValues.push_back(CPLStrdup(
    2063             :                         ""));  // guard value to avoid index 0 to be used
    2064          72 :                     m_aoMapIndexedKeys[psKD->pszK] = psKD;
    2065          72 :                     m_asKeys.push_back(psKD);
    2066             :                 }
    2067             :             }
    2068             :             else
    2069             :             {
    2070         134 :                 psKD = oIterK->second;
    2071             :             }
    2072             : 
    2073         208 :             if (psKD)
    2074             :             {
    2075         206 :                 psKD->nOccurrences++;
    2076         206 :                 m_pasAccumulatedTags[m_nAccumulatedTags].bKIsIndex = TRUE;
    2077         206 :                 m_pasAccumulatedTags[m_nAccumulatedTags].uKey.nKeyIndex =
    2078         206 :                     psKD->nKeyIndex;
    2079             :             }
    2080             : 
    2081         414 :             if (psKD != nullptr &&
    2082         206 :                 psKD->asValues.size() < 1 + MAX_INDEXED_VALUES_PER_KEY)
    2083             :             {
    2084         204 :                 int nValueIndex = 0;
    2085         204 :                 auto oIterV = psKD->anMapV.find(pszV);
    2086         204 :                 if (oIterV == psKD->anMapV.end())
    2087             :                 {
    2088          96 :                     char *pszVDup = CPLStrdup(pszV);
    2089          96 :                     nValueIndex = static_cast<int>(psKD->asValues.size());
    2090          96 :                     psKD->anMapV[pszVDup] = nValueIndex;
    2091          96 :                     psKD->asValues.push_back(pszVDup);
    2092             :                 }
    2093             :                 else
    2094         108 :                     nValueIndex = oIterV->second;
    2095             : 
    2096         204 :                 m_pasAccumulatedTags[m_nAccumulatedTags].bVIsIndex = TRUE;
    2097         204 :                 m_pasAccumulatedTags[m_nAccumulatedTags].uVal.nValueIndex =
    2098             :                     nValueIndex;
    2099             :             }
    2100             :             else
    2101             :             {
    2102           4 :                 const int nLenV = static_cast<int>(strlen(pszV)) + 1;
    2103             : 
    2104           6 :                 if (psKD != nullptr &&
    2105           2 :                     psKD->asValues.size() == 1 + MAX_INDEXED_VALUES_PER_KEY)
    2106             :                 {
    2107           2 :                     CPLDebug("OSM", "More than %d different values for tag %s",
    2108             :                              MAX_INDEXED_VALUES_PER_KEY, pszK);
    2109             :                     // To avoid next warnings.
    2110           2 :                     psKD->asValues.push_back(CPLStrdup(""));
    2111             :                 }
    2112             : 
    2113           4 :                 if (nNonRedundantValuesLen + nLenV > MAX_NON_REDUNDANT_VALUES)
    2114             :                 {
    2115           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2116             :                              "Too many/too long values found");
    2117           0 :                     continue;
    2118             :                 }
    2119           4 :                 memcpy(pabyNonRedundantValues + nNonRedundantValuesLen, pszV,
    2120             :                        nLenV);
    2121           4 :                 m_pasAccumulatedTags[m_nAccumulatedTags].bVIsIndex = FALSE;
    2122           4 :                 m_pasAccumulatedTags[m_nAccumulatedTags]
    2123           4 :                     .uVal.nOffsetInpabyNonRedundantValues =
    2124           4 :                     nNonRedundantValuesLen;
    2125           4 :                 nNonRedundantValuesLen += nLenV;
    2126             :             }
    2127         208 :             m_nAccumulatedTags++;
    2128             : 
    2129         208 :             nTagCount++;
    2130         208 :             if (nTagCount == MAX_COUNT_FOR_TAGS_IN_WAY)
    2131           0 :                 break;
    2132             :         }
    2133             : 
    2134         186 :         psWayFeaturePairs->nTags = nTagCount;
    2135             :     }
    2136             :     else
    2137             :     {
    2138         439 :         psWayFeaturePairs->sInfo.ts.nTimeStamp = 0;
    2139         439 :         psWayFeaturePairs->sInfo.nChangeset = 0;
    2140         439 :         psWayFeaturePairs->sInfo.nVersion = 0;
    2141         439 :         psWayFeaturePairs->sInfo.nUID = 0;
    2142         439 :         psWayFeaturePairs->sInfo.bTimeStampIsStr = false;
    2143         439 :         psWayFeaturePairs->sInfo.pszUserSID = "";
    2144             : 
    2145         439 :         psWayFeaturePairs->nTags = 0;
    2146         439 :         psWayFeaturePairs->pasTags = nullptr;
    2147             :     }
    2148             : 
    2149         625 :     m_nWayFeaturePairs++;
    2150             : 
    2151         625 :     memcpy(m_panUnsortedReqIds + m_nUnsortedReqIds, psWay->panNodeRefs,
    2152         625 :            sizeof(GIntBig) * (psWay->nRefs - (bIsArea ? 1 : 0)));
    2153         625 :     m_nUnsortedReqIds += (psWay->nRefs - (bIsArea ? 1 : 0));
    2154             : }
    2155             : 
    2156         717 : static void OGROSMNotifyWay(OSMWay *psWay, OSMContext * /* psOSMContext */,
    2157             :                             void *user_data)
    2158             : {
    2159         717 :     static_cast<OGROSMDataSource *>(user_data)->NotifyWay(psWay);
    2160         717 : }
    2161             : 
    2162             : /************************************************************************/
    2163             : /*                            LookupWays()                              */
    2164             : /************************************************************************/
    2165             : 
    2166         100 : unsigned int OGROSMDataSource::LookupWays(
    2167             :     std::map<GIntBig, std::pair<int, void *>> &aoMapWays,
    2168             :     OSMRelation *psRelation)
    2169             : {
    2170         100 :     unsigned int nFound = 0;
    2171         100 :     unsigned int iCur = 0;
    2172             : 
    2173         200 :     while (iCur < psRelation->nMembers)
    2174             :     {
    2175         100 :         unsigned int nToQuery = 0;
    2176         100 :         unsigned int i = iCur;  // Used after for.
    2177         280 :         for (; i < psRelation->nMembers; i++)
    2178             :         {
    2179         180 :             if (psRelation->pasMembers[i].eType == MEMBER_WAY &&
    2180         165 :                 strcmp(psRelation->pasMembers[i].pszRole, "subarea") != 0)
    2181             :             {
    2182         165 :                 nToQuery++;
    2183         165 :                 if (nToQuery ==
    2184             :                     static_cast<unsigned int>(LIMIT_IDS_PER_REQUEST))
    2185             :                 {
    2186           0 :                     break;
    2187             :                 }
    2188             :             }
    2189             :         }
    2190             : 
    2191         100 :         if (nToQuery == 0)
    2192           0 :             break;
    2193             : 
    2194         100 :         unsigned int iLastI = (i == psRelation->nMembers) ? i : i + 1;
    2195             : 
    2196         100 :         sqlite3_stmt *hStmt = m_pahSelectWayStmt[nToQuery - 1];
    2197         100 :         unsigned int nBindIndex = 1;
    2198         280 :         for (i = iCur; i < iLastI; i++)
    2199             :         {
    2200         180 :             if (psRelation->pasMembers[i].eType == MEMBER_WAY &&
    2201         165 :                 strcmp(psRelation->pasMembers[i].pszRole, "subarea") != 0)
    2202             :             {
    2203         165 :                 sqlite3_bind_int64(hStmt, nBindIndex,
    2204         165 :                                    psRelation->pasMembers[i].nID);
    2205         165 :                 nBindIndex++;
    2206             :             }
    2207             :         }
    2208         100 :         iCur = iLastI;
    2209             : 
    2210         236 :         while (sqlite3_step(hStmt) == SQLITE_ROW)
    2211             :         {
    2212         136 :             GIntBig id = sqlite3_column_int64(hStmt, 0);
    2213         136 :             if (aoMapWays.find(id) == aoMapWays.end())
    2214             :             {
    2215         136 :                 int nBlobSize = sqlite3_column_bytes(hStmt, 1);
    2216         136 :                 const void *blob = sqlite3_column_blob(hStmt, 1);
    2217         136 :                 void *blob_dup = CPLMalloc(nBlobSize);
    2218         136 :                 memcpy(blob_dup, blob, nBlobSize);
    2219         136 :                 aoMapWays[id] = std::pair(nBlobSize, blob_dup);
    2220             :             }
    2221         136 :             nFound++;
    2222             :         }
    2223             : 
    2224         100 :         sqlite3_reset(hStmt);
    2225             :     }
    2226             : 
    2227         100 :     return nFound;
    2228             : }
    2229             : 
    2230             : /************************************************************************/
    2231             : /*                          BuildMultiPolygon()                         */
    2232             : /************************************************************************/
    2233             : 
    2234          65 : OGRGeometry *OGROSMDataSource::BuildMultiPolygon(OSMRelation *psRelation,
    2235             :                                                  unsigned int *pnTags,
    2236             :                                                  OSMTag *pasTags)
    2237             : {
    2238         130 :     std::map<GIntBig, std::pair<int, void *>> aoMapWays;
    2239          65 :     LookupWays(aoMapWays, psRelation);
    2240             : 
    2241          65 :     bool bMissing = false;
    2242             : 
    2243         168 :     for (unsigned int i = 0; i < psRelation->nMembers; i++)
    2244             :     {
    2245         127 :         if (psRelation->pasMembers[i].eType == MEMBER_WAY &&
    2246         127 :             strcmp(psRelation->pasMembers[i].pszRole, "subarea") != 0)
    2247             :         {
    2248         127 :             if (aoMapWays.find(psRelation->pasMembers[i].nID) ==
    2249         254 :                 aoMapWays.end())
    2250             :             {
    2251          24 :                 CPLDebug("OSM",
    2252             :                          "Relation " CPL_FRMT_GIB
    2253             :                          " has missing ways. Ignoring it",
    2254             :                          psRelation->nID);
    2255          24 :                 bMissing = true;
    2256          24 :                 break;
    2257             :             }
    2258             :         }
    2259             :     }
    2260             : 
    2261          65 :     if (bMissing)
    2262             :     {
    2263             :         // cppcheck-suppress constVariableReference
    2264          45 :         for (auto &oIter : aoMapWays)
    2265          21 :             CPLFree(oIter.second.second);
    2266             : 
    2267          24 :         return nullptr;
    2268             :     }
    2269             : 
    2270          41 :     OGRMultiLineString *poMLS = new OGRMultiLineString();
    2271             :     OGRGeometry **papoPolygons = static_cast<OGRGeometry **>(
    2272          41 :         CPLMalloc(sizeof(OGRGeometry *) * psRelation->nMembers));
    2273          41 :     int nPolys = 0;
    2274             : 
    2275          41 :     if (pnTags != nullptr)
    2276          21 :         *pnTags = 0;
    2277             : 
    2278         123 :     for (unsigned int i = 0; i < psRelation->nMembers; i++)
    2279             :     {
    2280          82 :         if (psRelation->pasMembers[i].eType == MEMBER_WAY &&
    2281          82 :             strcmp(psRelation->pasMembers[i].pszRole, "subarea") != 0)
    2282             :         {
    2283          82 :             const auto &oGeom = aoMapWays[psRelation->pasMembers[i].nID];
    2284             : 
    2285          82 :             if (pnTags != nullptr && *pnTags == 0 &&
    2286          21 :                 strcmp(psRelation->pasMembers[i].pszRole, "outer") == 0)
    2287             :             {
    2288             :                 // This backup in m_abyWayBuffer is crucial for safe memory
    2289             :                 // usage, as pasTags[].pszV will point to it !
    2290          21 :                 m_abyWayBuffer.clear();
    2291           0 :                 m_abyWayBuffer.insert(m_abyWayBuffer.end(),
    2292          21 :                                       static_cast<const GByte *>(oGeom.second),
    2293          21 :                                       static_cast<const GByte *>(oGeom.second) +
    2294          21 :                                           oGeom.first);
    2295             : 
    2296          21 :                 UncompressWay(oGeom.first, m_abyWayBuffer.data(), nullptr,
    2297          21 :                               m_asLonLatCache, pnTags, pasTags, nullptr);
    2298             :             }
    2299             :             else
    2300             :             {
    2301          61 :                 UncompressWay(oGeom.first,
    2302          61 :                               static_cast<const GByte *>(oGeom.second), nullptr,
    2303          61 :                               m_asLonLatCache, nullptr, nullptr, nullptr);
    2304             :             }
    2305             : 
    2306          82 :             OGRLineString *poLS = nullptr;
    2307             : 
    2308          82 :             if (!m_asLonLatCache.empty() &&
    2309         164 :                 m_asLonLatCache.front().nLon == m_asLonLatCache.back().nLon &&
    2310          82 :                 m_asLonLatCache.front().nLat == m_asLonLatCache.back().nLat)
    2311             :             {
    2312          82 :                 OGRPolygon *poPoly = new OGRPolygon();
    2313          82 :                 OGRLinearRing *poRing = new OGRLinearRing();
    2314          82 :                 poPoly->addRingDirectly(poRing);
    2315          82 :                 papoPolygons[nPolys++] = poPoly;
    2316          82 :                 poLS = poRing;
    2317             : 
    2318          82 :                 if (strcmp(psRelation->pasMembers[i].pszRole, "outer") == 0)
    2319             :                 {
    2320          41 :                     sqlite3_bind_int64(m_hDeletePolygonsStandaloneStmt, 1,
    2321          41 :                                        psRelation->pasMembers[i].nID);
    2322          41 :                     CPL_IGNORE_RET_VAL(
    2323          41 :                         sqlite3_step(m_hDeletePolygonsStandaloneStmt));
    2324          41 :                     sqlite3_reset(m_hDeletePolygonsStandaloneStmt);
    2325             :                 }
    2326             :             }
    2327             :             else
    2328             :             {
    2329           0 :                 poLS = new OGRLineString();
    2330           0 :                 poMLS->addGeometryDirectly(poLS);
    2331             :             }
    2332             : 
    2333          82 :             const int nPoints = static_cast<int>(m_asLonLatCache.size());
    2334          82 :             poLS->setNumPoints(nPoints);
    2335         492 :             for (int j = 0; j < nPoints; j++)
    2336             :             {
    2337         410 :                 poLS->setPoint(j, INT_TO_DBL(m_asLonLatCache[j].nLon),
    2338         410 :                                INT_TO_DBL(m_asLonLatCache[j].nLat));
    2339             :             }
    2340             :         }
    2341             :     }
    2342             : 
    2343          41 :     if (poMLS->getNumGeometries() > 0)
    2344             :     {
    2345           0 :         OGRGeometryH hPoly = OGRBuildPolygonFromEdges((OGRGeometryH)poMLS, TRUE,
    2346             :                                                       FALSE, 0, nullptr);
    2347           0 :         if (hPoly != nullptr && OGR_G_GetGeometryType(hPoly) == wkbPolygon)
    2348             :         {
    2349             :             OGRPolygon *poSuperPoly =
    2350           0 :                 OGRGeometry::FromHandle(hPoly)->toPolygon();
    2351           0 :             for (unsigned int i = 0;
    2352           0 :                  i < 1 + (unsigned int)poSuperPoly->getNumInteriorRings(); i++)
    2353             :             {
    2354             :                 OGRLinearRing *poRing =
    2355           0 :                     (i == 0) ? poSuperPoly->getExteriorRing()
    2356           0 :                              : poSuperPoly->getInteriorRing(i - 1);
    2357           0 :                 if (poRing != nullptr && poRing->getNumPoints() >= 4 &&
    2358           0 :                     poRing->getX(0) ==
    2359           0 :                         poRing->getX(poRing->getNumPoints() - 1) &&
    2360           0 :                     poRing->getY(0) == poRing->getY(poRing->getNumPoints() - 1))
    2361             :                 {
    2362           0 :                     OGRPolygon *poPoly = new OGRPolygon();
    2363           0 :                     poPoly->addRing(poRing);
    2364           0 :                     papoPolygons[nPolys++] = poPoly;
    2365             :                 }
    2366             :             }
    2367             :         }
    2368             : 
    2369           0 :         OGR_G_DestroyGeometry(hPoly);
    2370             :     }
    2371          41 :     delete poMLS;
    2372             : 
    2373          41 :     OGRGeometry *poRet = nullptr;
    2374             : 
    2375          41 :     if (nPolys > 0)
    2376             :     {
    2377          41 :         int bIsValidGeometry = FALSE;
    2378          41 :         const char *apszOptions[2] = {"METHOD=DEFAULT", nullptr};
    2379          41 :         OGRGeometry *poGeom = OGRGeometryFactory::organizePolygons(
    2380             :             papoPolygons, nPolys, &bIsValidGeometry, apszOptions);
    2381             : 
    2382          41 :         if (poGeom != nullptr && poGeom->getGeometryType() == wkbPolygon)
    2383             :         {
    2384          41 :             OGRMultiPolygon *poMulti = new OGRMultiPolygon();
    2385          41 :             poMulti->addGeometryDirectly(poGeom);
    2386          41 :             poGeom = poMulti;
    2387             :         }
    2388             : 
    2389          41 :         if (poGeom != nullptr && poGeom->getGeometryType() == wkbMultiPolygon)
    2390             :         {
    2391          41 :             poRet = poGeom;
    2392             :         }
    2393             :         else
    2394             :         {
    2395           0 :             CPLDebug("OSM",
    2396             :                      "Relation " CPL_FRMT_GIB
    2397             :                      ": Geometry has incompatible type : %s",
    2398             :                      psRelation->nID,
    2399             :                      poGeom != nullptr
    2400           0 :                          ? OGR_G_GetGeometryName(OGRGeometry::ToHandle(poGeom))
    2401             :                          : "null");
    2402           0 :             delete poGeom;
    2403             :         }
    2404             :     }
    2405             : 
    2406          41 :     CPLFree(papoPolygons);
    2407             : 
    2408             :     // cppcheck-suppress constVariableReference
    2409         123 :     for (auto &oIter : aoMapWays)
    2410          82 :         CPLFree(oIter.second.second);
    2411             : 
    2412          41 :     return poRet;
    2413             : }
    2414             : 
    2415             : /************************************************************************/
    2416             : /*                          BuildGeometryCollection()                   */
    2417             : /************************************************************************/
    2418             : 
    2419          35 : OGRGeometry *OGROSMDataSource::BuildGeometryCollection(OSMRelation *psRelation,
    2420             :                                                        int bMultiLineString)
    2421             : {
    2422          35 :     std::map<GIntBig, std::pair<int, void *>> aoMapWays;
    2423          35 :     LookupWays(aoMapWays, psRelation);
    2424             : 
    2425             :     OGRGeometryCollection *poColl = (bMultiLineString)
    2426          51 :                                         ? new OGRMultiLineString()
    2427          16 :                                         : new OGRGeometryCollection();
    2428             : 
    2429          85 :     for (unsigned int i = 0; i < psRelation->nMembers; i++)
    2430             :     {
    2431          50 :         if (psRelation->pasMembers[i].eType == MEMBER_NODE && !bMultiLineString)
    2432             :         {
    2433          15 :             m_nUnsortedReqIds = 1;
    2434          15 :             m_panUnsortedReqIds[0] = psRelation->pasMembers[i].nID;
    2435          15 :             LookupNodes();
    2436          15 :             if (m_nReqIds == 1)
    2437             :             {
    2438          14 :                 poColl->addGeometryDirectly(
    2439          14 :                     new OGRPoint(INT_TO_DBL(m_pasLonLatArray[0].nLon),
    2440          14 :                                  INT_TO_DBL(m_pasLonLatArray[0].nLat)));
    2441             :             }
    2442             :         }
    2443         105 :         else if (psRelation->pasMembers[i].eType == MEMBER_WAY &&
    2444          70 :                  strcmp(psRelation->pasMembers[i].pszRole, "subarea") != 0 &&
    2445          35 :                  aoMapWays.find(psRelation->pasMembers[i].nID) !=
    2446          70 :                      aoMapWays.end())
    2447             :         {
    2448          33 :             const auto &oGeom = aoMapWays[psRelation->pasMembers[i].nID];
    2449             : 
    2450          33 :             bool bIsArea = false;
    2451          33 :             UncompressWay(oGeom.first, reinterpret_cast<GByte *>(oGeom.second),
    2452          33 :                           &bIsArea, m_asLonLatCache, nullptr, nullptr, nullptr);
    2453          33 :             OGRLineString *poLS = nullptr;
    2454          33 :             if (bIsArea && !bMultiLineString)
    2455             :             {
    2456           1 :                 OGRLinearRing *poLR = new OGRLinearRing();
    2457           1 :                 OGRPolygon *poPoly = new OGRPolygon();
    2458           1 :                 poPoly->addRingDirectly(poLR);
    2459           1 :                 poColl->addGeometryDirectly(poPoly);
    2460           1 :                 poLS = poLR;
    2461             :             }
    2462             :             else
    2463             :             {
    2464          32 :                 poLS = new OGRLineString();
    2465          32 :                 poColl->addGeometryDirectly(poLS);
    2466             :             }
    2467             : 
    2468          33 :             const int nPoints = static_cast<int>(m_asLonLatCache.size());
    2469          33 :             poLS->setNumPoints(nPoints);
    2470         101 :             for (int j = 0; j < nPoints; j++)
    2471             :             {
    2472          68 :                 poLS->setPoint(j, INT_TO_DBL(m_asLonLatCache[j].nLon),
    2473          68 :                                INT_TO_DBL(m_asLonLatCache[j].nLat));
    2474             :             }
    2475             :         }
    2476             :     }
    2477             : 
    2478          35 :     if (poColl->getNumGeometries() == 0)
    2479             :     {
    2480           2 :         delete poColl;
    2481           2 :         poColl = nullptr;
    2482             :     }
    2483             : 
    2484             :     // cppcheck-suppress constVariableReference
    2485          68 :     for (auto &oIter : aoMapWays)
    2486          33 :         CPLFree(oIter.second.second);
    2487             : 
    2488          70 :     return poColl;
    2489             : }
    2490             : 
    2491             : /************************************************************************/
    2492             : /*                            NotifyRelation()                          */
    2493             : /************************************************************************/
    2494             : 
    2495         166 : void OGROSMDataSource::NotifyRelation(OSMRelation *psRelation)
    2496             : {
    2497         166 :     if (m_nWayFeaturePairs != 0)
    2498          27 :         ProcessWaysBatch();
    2499             : 
    2500         166 :     m_nRelationsProcessed++;
    2501         166 :     if ((m_nRelationsProcessed % 10000) == 0)
    2502             :     {
    2503           0 :         CPLDebug("OSM", "Relations processed : %d", m_nRelationsProcessed);
    2504             : #ifdef DEBUG_MEM_USAGE
    2505             :         CPLDebug("OSM", "GetMaxTotalAllocs() = " CPL_FRMT_GUIB,
    2506             :                  static_cast<GUIntBig>(GetMaxTotalAllocs()));
    2507             : #endif
    2508             :     }
    2509             : 
    2510         166 :     if (!m_bUseWaysIndex)
    2511          66 :         return;
    2512             : 
    2513         121 :     bool bMultiPolygon = false;
    2514         121 :     bool bMultiLineString = false;
    2515         121 :     bool bInterestingTagFound = false;
    2516         121 :     const char *pszTypeV = nullptr;
    2517         266 :     for (unsigned int i = 0; i < psRelation->nTags; i++)
    2518             :     {
    2519         145 :         const char *pszK = psRelation->pasTags[i].pszK;
    2520         145 :         if (strcmp(pszK, "type") == 0)
    2521             :         {
    2522         120 :             const char *pszV = psRelation->pasTags[i].pszV;
    2523         120 :             pszTypeV = pszV;
    2524         120 :             if (strcmp(pszV, "multipolygon") == 0 ||
    2525          48 :                 strcmp(pszV, "boundary") == 0)
    2526             :             {
    2527          72 :                 bMultiPolygon = true;
    2528             :             }
    2529          48 :             else if (strcmp(pszV, "multilinestring") == 0 ||
    2530          48 :                      strcmp(pszV, "route") == 0)
    2531             :             {
    2532          24 :                 bMultiLineString = true;
    2533             :             }
    2534             :         }
    2535          25 :         else if (strcmp(pszK, "created_by") != 0)
    2536          25 :             bInterestingTagFound = true;
    2537             :     }
    2538             : 
    2539             :     // Optimization: If we have an attribute filter, that does not require
    2540             :     // geometry, then we can just evaluate the attribute filter without the
    2541             :     // geometry.
    2542         121 :     const int iCurLayer = bMultiPolygon      ? IDX_LYR_MULTIPOLYGONS
    2543             :                           : bMultiLineString ? IDX_LYR_MULTILINESTRINGS
    2544             :                                              : IDX_LYR_OTHER_RELATIONS;
    2545         121 :     if (!m_papoLayers[iCurLayer]->IsUserInterested())
    2546          19 :         return;
    2547             : 
    2548         102 :     OGRFeature *poFeature = nullptr;
    2549             : 
    2550         124 :     if (!(bMultiPolygon && !bInterestingTagFound) &&
    2551             :         // We cannot do early filtering for multipolygon that has no
    2552             :         // interesting tag, since we may fetch attributes from ways.
    2553         226 :         m_papoLayers[iCurLayer]->HasAttributeFilter() &&
    2554           5 :         !m_papoLayers[iCurLayer]->AttributeFilterEvaluationNeedsGeometry())
    2555             :     {
    2556           5 :         poFeature = new OGRFeature(m_papoLayers[iCurLayer]->GetLayerDefn());
    2557             : 
    2558           5 :         m_papoLayers[iCurLayer]->SetFieldsFromTags(
    2559             :             poFeature, psRelation->nID, false, psRelation->nTags,
    2560             :             psRelation->pasTags, &psRelation->sInfo);
    2561             : 
    2562           5 :         if (!m_papoLayers[iCurLayer]->EvaluateAttributeFilter(poFeature))
    2563             :         {
    2564           2 :             delete poFeature;
    2565           2 :             return;
    2566             :         }
    2567             :     }
    2568             : 
    2569         100 :     OGRGeometry *poGeom = nullptr;
    2570             : 
    2571         100 :     unsigned int nExtraTags = 0;
    2572             :     OSMTag pasExtraTags[1 + MAX_COUNT_FOR_TAGS_IN_WAY];
    2573             : 
    2574         100 :     if (bMultiPolygon)
    2575             :     {
    2576          65 :         if (!bInterestingTagFound)
    2577             :         {
    2578          44 :             poGeom = BuildMultiPolygon(psRelation, &nExtraTags, pasExtraTags);
    2579          44 :             CPLAssert(nExtraTags <= MAX_COUNT_FOR_TAGS_IN_WAY);
    2580          44 :             pasExtraTags[nExtraTags].pszK = "type";
    2581          44 :             pasExtraTags[nExtraTags].pszV = pszTypeV;
    2582          44 :             nExtraTags++;
    2583             :         }
    2584             :         else
    2585          21 :             poGeom = BuildMultiPolygon(psRelation, nullptr, nullptr);
    2586             :     }
    2587             :     else
    2588          35 :         poGeom = BuildGeometryCollection(psRelation, bMultiLineString);
    2589             : 
    2590         100 :     if (poGeom != nullptr)
    2591             :     {
    2592          74 :         bool bAttrFilterAlreadyEvaluated = true;
    2593          74 :         if (poFeature == nullptr)
    2594             :         {
    2595          71 :             poFeature = new OGRFeature(m_papoLayers[iCurLayer]->GetLayerDefn());
    2596             : 
    2597         142 :             m_papoLayers[iCurLayer]->SetFieldsFromTags(
    2598             :                 poFeature, psRelation->nID, false,
    2599          71 :                 nExtraTags ? nExtraTags : psRelation->nTags,
    2600          71 :                 nExtraTags ? pasExtraTags : psRelation->pasTags,
    2601             :                 &psRelation->sInfo);
    2602             : 
    2603          71 :             bAttrFilterAlreadyEvaluated = false;
    2604             :         }
    2605             : 
    2606          74 :         poFeature->SetGeometryDirectly(poGeom);
    2607             : 
    2608          74 :         int bFilteredOut = FALSE;
    2609          74 :         if (!m_papoLayers[iCurLayer]->AddFeature(
    2610             :                 poFeature, bAttrFilterAlreadyEvaluated, &bFilteredOut,
    2611          74 :                 !m_bFeatureAdded))
    2612           0 :             m_bStopParsing = true;
    2613          74 :         else if (!bFilteredOut)
    2614          72 :             m_bFeatureAdded = true;
    2615             :     }
    2616             :     else
    2617             :     {
    2618          26 :         delete poFeature;
    2619             :     }
    2620             : }
    2621             : 
    2622         166 : static void OGROSMNotifyRelation(OSMRelation *psRelation,
    2623             :                                  OSMContext * /* psOSMContext */,
    2624             :                                  void *user_data)
    2625             : {
    2626         166 :     static_cast<OGROSMDataSource *>(user_data)->NotifyRelation(psRelation);
    2627         166 : }
    2628             : 
    2629             : /************************************************************************/
    2630             : /*                      ProcessPolygonsStandalone()                     */
    2631             : /************************************************************************/
    2632             : 
    2633          41 : void OGROSMDataSource::ProcessPolygonsStandalone()
    2634             : {
    2635          41 :     unsigned int nTags = 0;
    2636             :     OSMTag pasTags[MAX_COUNT_FOR_TAGS_IN_WAY];
    2637             :     OSMInfo sInfo;
    2638             : 
    2639          41 :     sInfo.ts.nTimeStamp = 0;
    2640          41 :     sInfo.nChangeset = 0;
    2641          41 :     sInfo.nVersion = 0;
    2642          41 :     sInfo.nUID = 0;
    2643          41 :     sInfo.bTimeStampIsStr = false;
    2644          41 :     sInfo.pszUserSID = "";
    2645             : 
    2646          41 :     if (!m_bHasRowInPolygonsStandalone)
    2647          41 :         m_bHasRowInPolygonsStandalone =
    2648          41 :             sqlite3_step(m_hSelectPolygonsStandaloneStmt) == SQLITE_ROW;
    2649             : 
    2650          41 :     bool bFirst = true;
    2651             : 
    2652         182 :     while (m_bHasRowInPolygonsStandalone &&
    2653         141 :            m_papoLayers[IDX_LYR_MULTIPOLYGONS]->m_nFeatureArraySize < 10000)
    2654             :     {
    2655         141 :         if (bFirst)
    2656             :         {
    2657          23 :             CPLDebug("OSM", "Remaining standalone polygons");
    2658          23 :             bFirst = false;
    2659             :         }
    2660             : 
    2661         141 :         GIntBig id = sqlite3_column_int64(m_hSelectPolygonsStandaloneStmt, 0);
    2662             : 
    2663         141 :         sqlite3_bind_int64(m_pahSelectWayStmt[0], 1, id);
    2664         141 :         if (sqlite3_step(m_pahSelectWayStmt[0]) == SQLITE_ROW)
    2665             :         {
    2666         141 :             int nBlobSize = sqlite3_column_bytes(m_pahSelectWayStmt[0], 1);
    2667         141 :             const void *blob = sqlite3_column_blob(m_pahSelectWayStmt[0], 1);
    2668             : 
    2669             :             // coverity[tainted_data]
    2670         141 :             UncompressWay(nBlobSize, static_cast<const GByte *>(blob), nullptr,
    2671         141 :                           m_asLonLatCache, &nTags, pasTags, &sInfo);
    2672         141 :             CPLAssert(nTags <= MAX_COUNT_FOR_TAGS_IN_WAY);
    2673             : 
    2674         141 :             OGRMultiPolygon *poMulti = new OGRMultiPolygon();
    2675         141 :             OGRPolygon *poPoly = new OGRPolygon();
    2676         141 :             OGRLinearRing *poRing = new OGRLinearRing();
    2677         141 :             poMulti->addGeometryDirectly(poPoly);
    2678         141 :             poPoly->addRingDirectly(poRing);
    2679         141 :             OGRLineString *poLS = poRing;
    2680             : 
    2681         141 :             poLS->setNumPoints(static_cast<int>(m_asLonLatCache.size()));
    2682        1306 :             for (int j = 0; j < static_cast<int>(m_asLonLatCache.size()); j++)
    2683             :             {
    2684        1165 :                 poLS->setPoint(j, INT_TO_DBL(m_asLonLatCache[j].nLon),
    2685        1165 :                                INT_TO_DBL(m_asLonLatCache[j].nLat));
    2686             :             }
    2687             : 
    2688             :             OGRFeature *poFeature = new OGRFeature(
    2689         141 :                 m_papoLayers[IDX_LYR_MULTIPOLYGONS]->GetLayerDefn());
    2690             : 
    2691         141 :             m_papoLayers[IDX_LYR_MULTIPOLYGONS]->SetFieldsFromTags(
    2692             :                 poFeature, id, true, nTags, pasTags, &sInfo);
    2693             : 
    2694         141 :             poFeature->SetGeometryDirectly(poMulti);
    2695             : 
    2696         141 :             int bFilteredOut = FALSE;
    2697         141 :             if (!m_papoLayers[IDX_LYR_MULTIPOLYGONS]->AddFeature(
    2698         141 :                     poFeature, FALSE, &bFilteredOut, !m_bFeatureAdded))
    2699             :             {
    2700           0 :                 m_bStopParsing = true;
    2701           0 :                 break;
    2702             :             }
    2703         141 :             else if (!bFilteredOut)
    2704             :             {
    2705         138 :                 m_bFeatureAdded = true;
    2706             :             }
    2707             :         }
    2708             :         else
    2709             :         {
    2710           0 :             CPLAssert(false);
    2711             :         }
    2712             : 
    2713         141 :         sqlite3_reset(m_pahSelectWayStmt[0]);
    2714             : 
    2715         141 :         m_bHasRowInPolygonsStandalone =
    2716         141 :             sqlite3_step(m_hSelectPolygonsStandaloneStmt) == SQLITE_ROW;
    2717             :     }
    2718          41 : }
    2719             : 
    2720             : /************************************************************************/
    2721             : /*                             NotifyBounds()                           */
    2722             : /************************************************************************/
    2723             : 
    2724           6 : void OGROSMDataSource::NotifyBounds(double dfXMin, double dfYMin, double dfXMax,
    2725             :                                     double dfYMax)
    2726             : {
    2727           6 :     m_sExtent.MinX = dfXMin;
    2728           6 :     m_sExtent.MinY = dfYMin;
    2729           6 :     m_sExtent.MaxX = dfXMax;
    2730           6 :     m_sExtent.MaxY = dfYMax;
    2731           6 :     m_bExtentValid = true;
    2732             : 
    2733           6 :     CPLDebug("OSM", "Got bounds : minx=%f, miny=%f, maxx=%f, maxy=%f", dfXMin,
    2734             :              dfYMin, dfXMax, dfYMax);
    2735           6 : }
    2736             : 
    2737           6 : static void OGROSMNotifyBounds(double dfXMin, double dfYMin, double dfXMax,
    2738             :                                double dfYMax, OSMContext * /* psCtxt */,
    2739             :                                void *user_data)
    2740             : {
    2741           6 :     static_cast<OGROSMDataSource *>(user_data)->NotifyBounds(dfXMin, dfYMin,
    2742             :                                                              dfXMax, dfYMax);
    2743           6 : }
    2744             : 
    2745             : /************************************************************************/
    2746             : /*                                Open()                                */
    2747             : /************************************************************************/
    2748             : 
    2749          31 : int OGROSMDataSource::Open(const char *pszFilename, char **papszOpenOptionsIn)
    2750             : 
    2751             : {
    2752          31 :     m_pszName = CPLStrdup(pszFilename);
    2753             : 
    2754          31 :     m_psParser = OSM_Open(m_pszName, OGROSMNotifyNodes, OGROSMNotifyWay,
    2755             :                           OGROSMNotifyRelation, OGROSMNotifyBounds, this);
    2756          31 :     if (m_psParser == nullptr)
    2757           0 :         return FALSE;
    2758             : 
    2759          31 :     if (CPLFetchBool(papszOpenOptionsIn, "INTERLEAVED_READING", false))
    2760           0 :         m_bInterleavedReading = TRUE;
    2761             : 
    2762             :     /* The following 4 config options are only useful for debugging */
    2763          31 :     m_bIndexPoints = CPLTestBool(CPLGetConfigOption("OSM_INDEX_POINTS", "YES"));
    2764          31 :     m_bUsePointsIndex =
    2765          31 :         CPLTestBool(CPLGetConfigOption("OSM_USE_POINTS_INDEX", "YES"));
    2766          31 :     m_bIndexWays = CPLTestBool(CPLGetConfigOption("OSM_INDEX_WAYS", "YES"));
    2767          31 :     m_bUseWaysIndex =
    2768          31 :         CPLTestBool(CPLGetConfigOption("OSM_USE_WAYS_INDEX", "YES"));
    2769             : 
    2770          31 :     m_bCustomIndexing = CPLTestBool(CSLFetchNameValueDef(
    2771             :         papszOpenOptionsIn, "USE_CUSTOM_INDEXING",
    2772             :         CPLGetConfigOption("OSM_USE_CUSTOM_INDEXING", "YES")));
    2773          31 :     if (!m_bCustomIndexing)
    2774           2 :         CPLDebug("OSM", "Using SQLite indexing for points");
    2775          31 :     m_bCompressNodes = CPLTestBool(
    2776             :         CSLFetchNameValueDef(papszOpenOptionsIn, "COMPRESS_NODES",
    2777             :                              CPLGetConfigOption("OSM_COMPRESS_NODES", "NO")));
    2778          31 :     if (m_bCompressNodes)
    2779           1 :         CPLDebug("OSM", "Using compression for nodes DB");
    2780             : 
    2781          31 :     m_nLayers = 5;
    2782          31 :     m_papoLayers = static_cast<OGROSMLayer **>(
    2783          31 :         CPLMalloc(m_nLayers * sizeof(OGROSMLayer *)));
    2784             : 
    2785          31 :     m_papoLayers[IDX_LYR_POINTS] =
    2786          31 :         new OGROSMLayer(this, IDX_LYR_POINTS, "points");
    2787          31 :     m_papoLayers[IDX_LYR_POINTS]->GetLayerDefn()->SetGeomType(wkbPoint);
    2788             : 
    2789          31 :     m_papoLayers[IDX_LYR_LINES] = new OGROSMLayer(this, IDX_LYR_LINES, "lines");
    2790          31 :     m_papoLayers[IDX_LYR_LINES]->GetLayerDefn()->SetGeomType(wkbLineString);
    2791             : 
    2792          31 :     m_papoLayers[IDX_LYR_MULTILINESTRINGS] =
    2793          31 :         new OGROSMLayer(this, IDX_LYR_MULTILINESTRINGS, "multilinestrings");
    2794          31 :     m_papoLayers[IDX_LYR_MULTILINESTRINGS]->GetLayerDefn()->SetGeomType(
    2795          31 :         wkbMultiLineString);
    2796             : 
    2797          31 :     m_papoLayers[IDX_LYR_MULTIPOLYGONS] =
    2798          31 :         new OGROSMLayer(this, IDX_LYR_MULTIPOLYGONS, "multipolygons");
    2799          31 :     m_papoLayers[IDX_LYR_MULTIPOLYGONS]->GetLayerDefn()->SetGeomType(
    2800          31 :         wkbMultiPolygon);
    2801             : 
    2802          31 :     m_papoLayers[IDX_LYR_OTHER_RELATIONS] =
    2803          31 :         new OGROSMLayer(this, IDX_LYR_OTHER_RELATIONS, "other_relations");
    2804          31 :     m_papoLayers[IDX_LYR_OTHER_RELATIONS]->GetLayerDefn()->SetGeomType(
    2805          31 :         wkbGeometryCollection);
    2806             : 
    2807          31 :     if (!ParseConf(papszOpenOptionsIn))
    2808             :     {
    2809           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2810             :                  "Could not parse configuration file for OSM import");
    2811           0 :         return FALSE;
    2812             :     }
    2813             : 
    2814             :     const char *pszTagsFormat =
    2815          31 :         CSLFetchNameValue(papszOpenOptionsIn, "TAGS_FORMAT");
    2816          31 :     if (pszTagsFormat)
    2817             :     {
    2818           2 :         if (EQUAL(pszTagsFormat, "JSON"))
    2819           2 :             m_bTagsAsHSTORE = false;
    2820           0 :         else if (EQUAL(pszTagsFormat, "HSTORE"))
    2821           0 :             m_bTagsAsHSTORE = true;
    2822             :         else
    2823             :         {
    2824           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    2825             :                      "Invalid value for TAGS_FORMAT open option: %s",
    2826             :                      pszTagsFormat);
    2827             :         }
    2828             :     }
    2829             : 
    2830          31 :     const auto eTagsSubType = m_bTagsAsHSTORE ? OFSTNone : OFSTJSON;
    2831         186 :     for (int i = 0; i < m_nLayers; i++)
    2832             :     {
    2833         155 :         if (m_papoLayers[i]->HasAllTags())
    2834             :         {
    2835           2 :             m_papoLayers[i]->AddField("all_tags", OFTString, eTagsSubType);
    2836           2 :             if (m_papoLayers[i]->HasOtherTags())
    2837             :             {
    2838           2 :                 m_papoLayers[i]->SetHasOtherTags(false);
    2839             :             }
    2840             :         }
    2841         153 :         else if (m_papoLayers[i]->HasOtherTags())
    2842         153 :             m_papoLayers[i]->AddField("other_tags", OFTString, eTagsSubType);
    2843             :     }
    2844             : 
    2845          31 :     m_bNeedsToSaveWayInfo =
    2846          62 :         (m_papoLayers[IDX_LYR_MULTIPOLYGONS]->HasTimestamp() ||
    2847          62 :          m_papoLayers[IDX_LYR_MULTIPOLYGONS]->HasChangeset() ||
    2848          62 :          m_papoLayers[IDX_LYR_MULTIPOLYGONS]->HasVersion() ||
    2849          93 :          m_papoLayers[IDX_LYR_MULTIPOLYGONS]->HasUID() ||
    2850          31 :          m_papoLayers[IDX_LYR_MULTIPOLYGONS]->HasUser());
    2851             : 
    2852          31 :     m_panReqIds = static_cast<GIntBig *>(
    2853          31 :         VSI_MALLOC_VERBOSE(MAX_ACCUMULATED_NODES * sizeof(GIntBig)));
    2854             : #ifdef ENABLE_NODE_LOOKUP_BY_HASHING
    2855          31 :     m_panHashedIndexes = static_cast<int *>(
    2856          31 :         VSI_MALLOC_VERBOSE(HASHED_INDEXES_ARRAY_SIZE * sizeof(int)));
    2857          31 :     m_psCollisionBuckets = static_cast<CollisionBucket *>(VSI_MALLOC_VERBOSE(
    2858             :         COLLISION_BUCKET_ARRAY_SIZE * sizeof(CollisionBucket)));
    2859             : #endif
    2860          31 :     m_pasLonLatArray = static_cast<LonLat *>(
    2861          31 :         VSI_MALLOC_VERBOSE(MAX_ACCUMULATED_NODES * sizeof(LonLat)));
    2862          31 :     m_panUnsortedReqIds = static_cast<GIntBig *>(
    2863          31 :         VSI_MALLOC_VERBOSE(MAX_ACCUMULATED_NODES * sizeof(GIntBig)));
    2864          31 :     m_pasWayFeaturePairs = static_cast<WayFeaturePair *>(
    2865          31 :         VSI_MALLOC_VERBOSE(MAX_DELAYED_FEATURES * sizeof(WayFeaturePair)));
    2866          31 :     m_pasAccumulatedTags = static_cast<IndexedKVP *>(
    2867          31 :         VSI_MALLOC_VERBOSE(MAX_ACCUMULATED_TAGS * sizeof(IndexedKVP)));
    2868          31 :     pabyNonRedundantValues =
    2869          31 :         static_cast<GByte *>(VSI_MALLOC_VERBOSE(MAX_NON_REDUNDANT_VALUES));
    2870          31 :     pabyNonRedundantKeys =
    2871          31 :         static_cast<GByte *>(VSI_MALLOC_VERBOSE(MAX_NON_REDUNDANT_KEYS));
    2872          31 :     if (m_panReqIds == nullptr || m_pasLonLatArray == nullptr ||
    2873          31 :         m_panUnsortedReqIds == nullptr || m_pasWayFeaturePairs == nullptr ||
    2874          31 :         m_pasAccumulatedTags == nullptr || pabyNonRedundantValues == nullptr ||
    2875          31 :         pabyNonRedundantKeys == nullptr)
    2876             :     {
    2877           0 :         return FALSE;
    2878             :     }
    2879             : 
    2880          31 :     m_nMaxSizeForInMemoryDBInMB = atoi(CSLFetchNameValueDef(
    2881             :         papszOpenOptionsIn, "MAX_TMPFILE_SIZE",
    2882             :         CPLGetConfigOption("OSM_MAX_TMPFILE_SIZE", "100")));
    2883          31 :     GIntBig nSize =
    2884          31 :         static_cast<GIntBig>(m_nMaxSizeForInMemoryDBInMB) * 1024 * 1024;
    2885          62 :     if (nSize < 0 ||
    2886          31 :         static_cast<GUIntBig>(nSize) > std::numeric_limits<size_t>::max() / 2)
    2887             :     {
    2888           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2889             :                  "Invalid value for OSM_MAX_TMPFILE_SIZE. Using 100 instead.");
    2890           0 :         m_nMaxSizeForInMemoryDBInMB = 100;
    2891           0 :         nSize = static_cast<GIntBig>(m_nMaxSizeForInMemoryDBInMB) * 1024 * 1024;
    2892             :     }
    2893             : 
    2894          31 :     if (m_bCustomIndexing)
    2895             :     {
    2896          29 :         m_pabySector = static_cast<GByte *>(VSI_CALLOC_VERBOSE(1, SECTOR_SIZE));
    2897             : 
    2898          29 :         if (m_pabySector == nullptr)
    2899             :         {
    2900           0 :             return FALSE;
    2901             :         }
    2902             : 
    2903          29 :         m_bInMemoryNodesFile = true;
    2904             :         m_osNodesFilename.Printf("/vsimem/osm_importer/osm_temp_nodes_%p",
    2905          29 :                                  this);
    2906          29 :         m_fpNodes = VSIFOpenL(m_osNodesFilename, "wb+");
    2907          29 :         if (m_fpNodes == nullptr)
    2908             :         {
    2909           0 :             return FALSE;
    2910             :         }
    2911             : 
    2912          29 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    2913             :         bool bSuccess =
    2914          29 :             VSIFSeekL(m_fpNodes, (vsi_l_offset)(nSize * 3 / 4), SEEK_SET) == 0;
    2915          29 :         CPLPopErrorHandler();
    2916             : 
    2917          29 :         if (bSuccess)
    2918             :         {
    2919          29 :             VSIFSeekL(m_fpNodes, 0, SEEK_SET);
    2920          29 :             VSIFTruncateL(m_fpNodes, 0);
    2921             :         }
    2922             :         else
    2923             :         {
    2924           0 :             CPLDebug("OSM", "Not enough memory for in-memory file. "
    2925             :                             "Using disk temporary file instead.");
    2926             : 
    2927           0 :             VSIFCloseL(m_fpNodes);
    2928           0 :             m_fpNodes = nullptr;
    2929           0 :             VSIUnlink(m_osNodesFilename);
    2930             : 
    2931           0 :             m_bInMemoryNodesFile = false;
    2932           0 :             m_osNodesFilename = CPLGenerateTempFilename("osm_tmp_nodes");
    2933             : 
    2934           0 :             m_fpNodes = VSIFOpenL(m_osNodesFilename, "wb+");
    2935           0 :             if (m_fpNodes == nullptr)
    2936             :             {
    2937           0 :                 return FALSE;
    2938             :             }
    2939             : 
    2940             :             /* On Unix filesystems, you can remove a file even if it */
    2941             :             /* opened */
    2942             :             const char *pszVal =
    2943           0 :                 CPLGetConfigOption("OSM_UNLINK_TMPFILE", "YES");
    2944           0 :             if (EQUAL(pszVal, "YES"))
    2945             :             {
    2946           0 :                 CPLPushErrorHandler(CPLQuietErrorHandler);
    2947           0 :                 m_bMustUnlinkNodesFile = VSIUnlink(m_osNodesFilename) != 0;
    2948           0 :                 CPLPopErrorHandler();
    2949             :             }
    2950             : 
    2951           0 :             return FALSE;
    2952             :         }
    2953             :     }
    2954             : 
    2955          31 :     const bool bRet = CreateTempDB();
    2956          31 :     if (bRet)
    2957             :     {
    2958          62 :         CPLString osInterestLayers = GetInterestLayersForDSName(GetName());
    2959          31 :         if (!osInterestLayers.empty())
    2960             :         {
    2961           1 :             delete ExecuteSQL(osInterestLayers, nullptr, nullptr);
    2962             :         }
    2963             :     }
    2964          31 :     return bRet;
    2965             : }
    2966             : 
    2967             : /************************************************************************/
    2968             : /*                             CreateTempDB()                           */
    2969             : /************************************************************************/
    2970             : 
    2971          31 : bool OGROSMDataSource::CreateTempDB()
    2972             : {
    2973          31 :     char *pszErrMsg = nullptr;
    2974             : 
    2975          31 :     int rc = 0;
    2976          31 :     bool bIsExisting = false;
    2977          31 :     bool bSuccess = false;
    2978             : 
    2979             :     const char *pszExistingTmpFile =
    2980          31 :         CPLGetConfigOption("OSM_EXISTING_TMPFILE", nullptr);
    2981          31 :     if (pszExistingTmpFile != nullptr)
    2982             :     {
    2983           0 :         bSuccess = true;
    2984           0 :         bIsExisting = true;
    2985           0 :         rc = sqlite3_open_v2(pszExistingTmpFile, &m_hDB,
    2986             :                              SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX,
    2987             :                              nullptr);
    2988             :     }
    2989             :     else
    2990             :     {
    2991          31 :         m_osTmpDBName.Printf("/vsimem/osm_importer/osm_temp_%p.sqlite", this);
    2992             : 
    2993             :         // On 32 bit, the virtual memory space is scarce, so we need to
    2994             :         // reserve it right now. Will not hurt on 64 bit either.
    2995          31 :         VSILFILE *fp = VSIFOpenL(m_osTmpDBName, "wb");
    2996          31 :         if (fp)
    2997             :         {
    2998          31 :             GIntBig nSize =
    2999          31 :                 static_cast<GIntBig>(m_nMaxSizeForInMemoryDBInMB) * 1024 * 1024;
    3000          31 :             if (m_bCustomIndexing && m_bInMemoryNodesFile)
    3001          29 :                 nSize = nSize / 4;
    3002             : 
    3003          31 :             CPLPushErrorHandler(CPLQuietErrorHandler);
    3004          31 :             bSuccess =
    3005          31 :                 VSIFSeekL(fp, static_cast<vsi_l_offset>(nSize), SEEK_SET) == 0;
    3006          31 :             CPLPopErrorHandler();
    3007             : 
    3008          31 :             if (bSuccess)
    3009          31 :                 bSuccess = VSIFTruncateL(fp, 0) == 0;
    3010             : 
    3011          31 :             VSIFCloseL(fp);
    3012             : 
    3013          31 :             if (!bSuccess)
    3014             :             {
    3015           0 :                 CPLDebug("OSM", "Not enough memory for in-memory file. "
    3016             :                                 "Using disk temporary file instead.");
    3017           0 :                 VSIUnlink(m_osTmpDBName);
    3018             :             }
    3019             :         }
    3020             : 
    3021          31 :         if (bSuccess)
    3022             :         {
    3023          31 :             m_bInMemoryTmpDB = true;
    3024          31 :             m_pMyVFS = OGRSQLiteCreateVFS(nullptr, this);
    3025          31 :             sqlite3_vfs_register(m_pMyVFS, 0);
    3026          31 :             rc = sqlite3_open_v2(m_osTmpDBName.c_str(), &m_hDB,
    3027             :                                  SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
    3028             :                                      SQLITE_OPEN_NOMUTEX,
    3029          31 :                                  m_pMyVFS->zName);
    3030             :         }
    3031             :     }
    3032             : 
    3033          31 :     if (!bSuccess)
    3034             :     {
    3035           0 :         m_osTmpDBName = CPLGenerateTempFilename("osm_tmp");
    3036           0 :         rc = sqlite3_open(m_osTmpDBName.c_str(), &m_hDB);
    3037             : 
    3038             :         /* On Unix filesystems, you can remove a file even if it */
    3039             :         /* opened */
    3040           0 :         if (rc == SQLITE_OK)
    3041             :         {
    3042             :             const char *pszVal =
    3043           0 :                 CPLGetConfigOption("OSM_UNLINK_TMPFILE", "YES");
    3044           0 :             if (EQUAL(pszVal, "YES"))
    3045             :             {
    3046           0 :                 CPLPushErrorHandler(CPLQuietErrorHandler);
    3047           0 :                 m_bMustUnlink = VSIUnlink(m_osTmpDBName) != 0;
    3048           0 :                 CPLPopErrorHandler();
    3049             :             }
    3050             :         }
    3051             :     }
    3052             : 
    3053          31 :     if (rc != SQLITE_OK)
    3054             :     {
    3055           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "sqlite3_open(%s) failed: %s",
    3056             :                  m_osTmpDBName.c_str(), sqlite3_errmsg(m_hDB));
    3057           0 :         return false;
    3058             :     }
    3059             : 
    3060          31 :     if (!SetDBOptions())
    3061             :     {
    3062           0 :         return false;
    3063             :     }
    3064             : 
    3065          31 :     if (!bIsExisting)
    3066             :     {
    3067          31 :         rc = sqlite3_exec(
    3068             :             m_hDB, "CREATE TABLE nodes (id INTEGER PRIMARY KEY, coords BLOB)",
    3069             :             nullptr, nullptr, &pszErrMsg);
    3070          31 :         if (rc != SQLITE_OK)
    3071             :         {
    3072           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3073             :                      "Unable to create table nodes : %s", pszErrMsg);
    3074           0 :             sqlite3_free(pszErrMsg);
    3075           0 :             return false;
    3076             :         }
    3077             : 
    3078          31 :         rc = sqlite3_exec(
    3079             :             m_hDB, "CREATE TABLE ways (id INTEGER PRIMARY KEY, data BLOB)",
    3080             :             nullptr, nullptr, &pszErrMsg);
    3081          31 :         if (rc != SQLITE_OK)
    3082             :         {
    3083           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3084             :                      "Unable to create table ways : %s", pszErrMsg);
    3085           0 :             sqlite3_free(pszErrMsg);
    3086           0 :             return false;
    3087             :         }
    3088             : 
    3089          31 :         rc = sqlite3_exec(
    3090             :             m_hDB, "CREATE TABLE polygons_standalone (id INTEGER PRIMARY KEY)",
    3091             :             nullptr, nullptr, &pszErrMsg);
    3092          31 :         if (rc != SQLITE_OK)
    3093             :         {
    3094           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3095             :                      "Unable to create table polygons_standalone : %s",
    3096             :                      pszErrMsg);
    3097           0 :             sqlite3_free(pszErrMsg);
    3098           0 :             return false;
    3099             :         }
    3100             :     }
    3101             : 
    3102          31 :     return CreatePreparedStatements();
    3103             : }
    3104             : 
    3105             : /************************************************************************/
    3106             : /*                            SetDBOptions()                            */
    3107             : /************************************************************************/
    3108             : 
    3109          31 : bool OGROSMDataSource::SetDBOptions()
    3110             : {
    3111          31 :     char *pszErrMsg = nullptr;
    3112          31 :     int rc = sqlite3_exec(m_hDB, "PRAGMA synchronous = OFF", nullptr, nullptr,
    3113             :                           &pszErrMsg);
    3114          31 :     if (rc != SQLITE_OK)
    3115             :     {
    3116           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3117             :                  "Unable to run PRAGMA synchronous : %s", pszErrMsg);
    3118           0 :         sqlite3_free(pszErrMsg);
    3119           0 :         return false;
    3120             :     }
    3121             : 
    3122          31 :     rc = sqlite3_exec(m_hDB, "PRAGMA journal_mode = OFF", nullptr, nullptr,
    3123             :                       &pszErrMsg);
    3124          31 :     if (rc != SQLITE_OK)
    3125             :     {
    3126           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3127             :                  "Unable to run PRAGMA journal_mode : %s", pszErrMsg);
    3128           0 :         sqlite3_free(pszErrMsg);
    3129           0 :         return false;
    3130             :     }
    3131             : 
    3132          31 :     rc = sqlite3_exec(m_hDB, "PRAGMA temp_store = MEMORY", nullptr, nullptr,
    3133             :                       &pszErrMsg);
    3134          31 :     if (rc != SQLITE_OK)
    3135             :     {
    3136           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3137             :                  "Unable to run PRAGMA temp_store : %s", pszErrMsg);
    3138           0 :         sqlite3_free(pszErrMsg);
    3139           0 :         return false;
    3140             :     }
    3141             : 
    3142          31 :     SetCacheSize();
    3143             : 
    3144          31 :     if (!StartTransactionCacheDB())
    3145           0 :         return false;
    3146             : 
    3147          31 :     return true;
    3148             : }
    3149             : 
    3150             : /************************************************************************/
    3151             : /*                              SetCacheSize()                          */
    3152             : /************************************************************************/
    3153             : 
    3154          31 : void OGROSMDataSource::SetCacheSize()
    3155             : {
    3156             :     const char *pszSqliteCacheMB =
    3157          31 :         CPLGetConfigOption("OSM_SQLITE_CACHE", nullptr);
    3158             : 
    3159          31 :     if (pszSqliteCacheMB == nullptr)
    3160          31 :         return;
    3161             : 
    3162           0 :     char *pszErrMsg = nullptr;
    3163           0 :     char **papszResult = nullptr;
    3164           0 :     int nRowCount = 0;
    3165           0 :     int nColCount = 0;
    3166           0 :     int iSqlitePageSize = -1;
    3167           0 :     const GIntBig iSqliteCacheBytes =
    3168           0 :         static_cast<GIntBig>(atoi(pszSqliteCacheMB)) * 1024 * 1024;
    3169             : 
    3170             :     /* querying the current PageSize */
    3171           0 :     int rc = sqlite3_get_table(m_hDB, "PRAGMA page_size", &papszResult,
    3172             :                                &nRowCount, &nColCount, &pszErrMsg);
    3173           0 :     if (rc == SQLITE_OK)
    3174             :     {
    3175           0 :         for (int iRow = 1; iRow <= nRowCount; iRow++)
    3176             :         {
    3177           0 :             iSqlitePageSize = atoi(papszResult[(iRow * nColCount) + 0]);
    3178             :         }
    3179           0 :         sqlite3_free_table(papszResult);
    3180             :     }
    3181           0 :     if (iSqlitePageSize < 0)
    3182             :     {
    3183           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3184             :                  "Unable to run PRAGMA page_size : %s",
    3185           0 :                  pszErrMsg ? pszErrMsg : sqlite3_errmsg(m_hDB));
    3186           0 :         sqlite3_free(pszErrMsg);
    3187           0 :         return;
    3188             :     }
    3189           0 :     if (iSqlitePageSize == 0)
    3190           0 :         return;
    3191             : 
    3192             :     /* computing the CacheSize as #Pages */
    3193           0 :     const int iSqliteCachePages =
    3194           0 :         static_cast<int>(iSqliteCacheBytes / iSqlitePageSize);
    3195           0 :     if (iSqliteCachePages <= 0)
    3196           0 :         return;
    3197             : 
    3198           0 :     rc = sqlite3_exec(m_hDB,
    3199             :                       CPLSPrintf("PRAGMA cache_size = %d", iSqliteCachePages),
    3200             :                       nullptr, nullptr, &pszErrMsg);
    3201           0 :     if (rc != SQLITE_OK)
    3202             :     {
    3203           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    3204             :                  "Unrecognized value for PRAGMA cache_size : %s", pszErrMsg);
    3205           0 :         sqlite3_free(pszErrMsg);
    3206             :     }
    3207             : }
    3208             : 
    3209             : /************************************************************************/
    3210             : /*                        CreatePreparedStatements()                    */
    3211             : /************************************************************************/
    3212             : 
    3213          31 : bool OGROSMDataSource::CreatePreparedStatements()
    3214             : {
    3215             :     int rc =
    3216          31 :         sqlite3_prepare_v2(m_hDB, "INSERT INTO nodes (id, coords) VALUES (?,?)",
    3217             :                            -1, &m_hInsertNodeStmt, nullptr);
    3218          31 :     if (rc != SQLITE_OK)
    3219             :     {
    3220           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3221             :                  "sqlite3_prepare_v2() failed :  %s", sqlite3_errmsg(m_hDB));
    3222           0 :         return false;
    3223             :     }
    3224             : 
    3225          31 :     m_pahSelectNodeStmt = static_cast<sqlite3_stmt **>(
    3226          31 :         CPLCalloc(sizeof(sqlite3_stmt *), LIMIT_IDS_PER_REQUEST));
    3227             : 
    3228             :     char szTmp[LIMIT_IDS_PER_REQUEST * 2 + 128];
    3229          31 :     strcpy(szTmp, "SELECT id, coords FROM nodes WHERE id IN (");
    3230          31 :     int nLen = static_cast<int>(strlen(szTmp));
    3231        6231 :     for (int i = 0; i < LIMIT_IDS_PER_REQUEST; i++)
    3232             :     {
    3233        6200 :         if (i == 0)
    3234             :         {
    3235          31 :             strcpy(szTmp + nLen, "?) ORDER BY id ASC");
    3236          31 :             nLen += 2;
    3237             :         }
    3238             :         else
    3239             :         {
    3240        6169 :             strcpy(szTmp + nLen - 1, ",?) ORDER BY id ASC");
    3241        6169 :             nLen += 2;
    3242             :         }
    3243        6200 :         rc = sqlite3_prepare_v2(m_hDB, szTmp, -1, &m_pahSelectNodeStmt[i],
    3244             :                                 nullptr);
    3245        6200 :         if (rc != SQLITE_OK)
    3246             :         {
    3247           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3248             :                      "sqlite3_prepare_v2() failed :  %s",
    3249             :                      sqlite3_errmsg(m_hDB));
    3250           0 :             return false;
    3251             :         }
    3252             :     }
    3253             : 
    3254          31 :     rc = sqlite3_prepare_v2(m_hDB, "INSERT INTO ways (id, data) VALUES (?,?)",
    3255             :                             -1, &m_hInsertWayStmt, nullptr);
    3256          31 :     if (rc != SQLITE_OK)
    3257             :     {
    3258           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3259             :                  "sqlite3_prepare_v2() failed :  %s", sqlite3_errmsg(m_hDB));
    3260           0 :         return false;
    3261             :     }
    3262             : 
    3263          31 :     m_pahSelectWayStmt = static_cast<sqlite3_stmt **>(
    3264          31 :         CPLCalloc(sizeof(sqlite3_stmt *), LIMIT_IDS_PER_REQUEST));
    3265             : 
    3266          31 :     strcpy(szTmp, "SELECT id, data FROM ways WHERE id IN (");
    3267          31 :     nLen = static_cast<int>(strlen(szTmp));
    3268        6231 :     for (int i = 0; i < LIMIT_IDS_PER_REQUEST; i++)
    3269             :     {
    3270        6200 :         if (i == 0)
    3271             :         {
    3272          31 :             strcpy(szTmp + nLen, "?)");
    3273          31 :             nLen += 2;
    3274             :         }
    3275             :         else
    3276             :         {
    3277        6169 :             strcpy(szTmp + nLen - 1, ",?)");
    3278        6169 :             nLen += 2;
    3279             :         }
    3280        6200 :         rc = sqlite3_prepare_v2(m_hDB, szTmp, -1, &m_pahSelectWayStmt[i],
    3281             :                                 nullptr);
    3282        6200 :         if (rc != SQLITE_OK)
    3283             :         {
    3284           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3285             :                      "sqlite3_prepare_v2() failed :  %s",
    3286             :                      sqlite3_errmsg(m_hDB));
    3287           0 :             return false;
    3288             :         }
    3289             :     }
    3290             : 
    3291          31 :     rc = sqlite3_prepare_v2(m_hDB,
    3292             :                             "INSERT INTO polygons_standalone (id) VALUES (?)",
    3293             :                             -1, &m_hInsertPolygonsStandaloneStmt, nullptr);
    3294          31 :     if (rc != SQLITE_OK)
    3295             :     {
    3296           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3297             :                  "sqlite3_prepare_v2() failed :  %s", sqlite3_errmsg(m_hDB));
    3298           0 :         return false;
    3299             :     }
    3300             : 
    3301          31 :     rc = sqlite3_prepare_v2(m_hDB,
    3302             :                             "DELETE FROM polygons_standalone WHERE id = ?", -1,
    3303             :                             &m_hDeletePolygonsStandaloneStmt, nullptr);
    3304          31 :     if (rc != SQLITE_OK)
    3305             :     {
    3306           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3307             :                  "sqlite3_prepare_v2() failed :  %s", sqlite3_errmsg(m_hDB));
    3308           0 :         return false;
    3309             :     }
    3310             : 
    3311          31 :     rc = sqlite3_prepare_v2(m_hDB,
    3312             :                             "SELECT id FROM polygons_standalone ORDER BY id",
    3313             :                             -1, &m_hSelectPolygonsStandaloneStmt, nullptr);
    3314          31 :     if (rc != SQLITE_OK)
    3315             :     {
    3316           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3317             :                  "sqlite3_prepare_v2() failed :  %s", sqlite3_errmsg(m_hDB));
    3318           0 :         return false;
    3319             :     }
    3320             : 
    3321          31 :     return true;
    3322             : }
    3323             : 
    3324             : /************************************************************************/
    3325             : /*                      StartTransactionCacheDB()                       */
    3326             : /************************************************************************/
    3327             : 
    3328          31 : bool OGROSMDataSource::StartTransactionCacheDB()
    3329             : {
    3330          31 :     if (m_bInTransaction)
    3331           0 :         return false;
    3332             : 
    3333          31 :     char *pszErrMsg = nullptr;
    3334          31 :     int rc = sqlite3_exec(m_hDB, "BEGIN", nullptr, nullptr, &pszErrMsg);
    3335          31 :     if (rc != SQLITE_OK)
    3336             :     {
    3337           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3338             :                  "Unable to start transaction : %s", pszErrMsg);
    3339           0 :         sqlite3_free(pszErrMsg);
    3340           0 :         return false;
    3341             :     }
    3342             : 
    3343          31 :     m_bInTransaction = true;
    3344             : 
    3345          31 :     return true;
    3346             : }
    3347             : 
    3348             : /************************************************************************/
    3349             : /*                        CommitTransactionCacheDB()                    */
    3350             : /************************************************************************/
    3351             : 
    3352          31 : bool OGROSMDataSource::CommitTransactionCacheDB()
    3353             : {
    3354          31 :     if (!m_bInTransaction)
    3355           0 :         return false;
    3356             : 
    3357          31 :     m_bInTransaction = false;
    3358             : 
    3359          31 :     char *pszErrMsg = nullptr;
    3360          31 :     int rc = sqlite3_exec(m_hDB, "COMMIT", nullptr, nullptr, &pszErrMsg);
    3361          31 :     if (rc != SQLITE_OK)
    3362             :     {
    3363           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3364             :                  "Unable to commit transaction : %s", pszErrMsg);
    3365           0 :         sqlite3_free(pszErrMsg);
    3366           0 :         return false;
    3367             :     }
    3368             : 
    3369          31 :     return true;
    3370             : }
    3371             : 
    3372             : /************************************************************************/
    3373             : /*                     AddComputedAttributes()                          */
    3374             : /************************************************************************/
    3375             : 
    3376         151 : void OGROSMDataSource::AddComputedAttributes(
    3377             :     int iCurLayer, const std::vector<OGROSMComputedAttribute> &oAttributes)
    3378             : {
    3379         181 :     for (size_t i = 0; i < oAttributes.size(); i++)
    3380             :     {
    3381          30 :         if (!oAttributes[i].osSQL.empty())
    3382             :         {
    3383          60 :             m_papoLayers[iCurLayer]->AddComputedAttribute(oAttributes[i].osName,
    3384          30 :                                                           oAttributes[i].eType,
    3385          30 :                                                           oAttributes[i].osSQL);
    3386             :         }
    3387             :     }
    3388         151 : }
    3389             : 
    3390             : /************************************************************************/
    3391             : /*                           ParseConf()                                */
    3392             : /************************************************************************/
    3393             : 
    3394          31 : bool OGROSMDataSource::ParseConf(char **papszOpenOptionsIn)
    3395             : {
    3396             :     const char *pszFilename =
    3397          31 :         CSLFetchNameValueDef(papszOpenOptionsIn, "CONFIG_FILE",
    3398             :                              CPLGetConfigOption("OSM_CONFIG_FILE", nullptr));
    3399          31 :     if (pszFilename == nullptr)
    3400          29 :         pszFilename = CPLFindFile("gdal", "osmconf.ini");
    3401          31 :     if (pszFilename == nullptr)
    3402             :     {
    3403           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    3404             :                  "Cannot find osmconf.ini configuration file");
    3405           0 :         return false;
    3406             :     }
    3407             : 
    3408          31 :     VSILFILE *fpConf = VSIFOpenL(pszFilename, "rb");
    3409          31 :     if (fpConf == nullptr)
    3410           0 :         return false;
    3411             : 
    3412          31 :     const char *pszLine = nullptr;
    3413          31 :     int iCurLayer = -1;
    3414          31 :     std::vector<OGROSMComputedAttribute> oAttributes;
    3415             : 
    3416        3973 :     while ((pszLine = CPLReadLine2L(fpConf, -1, nullptr)) != nullptr)
    3417             :     {
    3418        3942 :         if (pszLine[0] == '#')
    3419        1868 :             continue;
    3420        2074 :         if (pszLine[0] == '[' && pszLine[strlen(pszLine) - 1] == ']')
    3421             :         {
    3422         151 :             if (iCurLayer >= 0)
    3423         120 :                 AddComputedAttributes(iCurLayer, oAttributes);
    3424         151 :             oAttributes.resize(0);
    3425             : 
    3426         151 :             iCurLayer = -1;
    3427         151 :             pszLine++;
    3428         151 :             ((char *)pszLine)[strlen(pszLine) - 1] = '\0'; /* Evil but OK */
    3429         451 :             for (int i = 0; i < m_nLayers; i++)
    3430             :             {
    3431         451 :                 if (strcmp(pszLine, m_papoLayers[i]->GetName()) == 0)
    3432             :                 {
    3433         151 :                     iCurLayer = i;
    3434         151 :                     break;
    3435             :                 }
    3436             :             }
    3437         151 :             if (iCurLayer < 0)
    3438             :             {
    3439           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    3440             :                          "Layer '%s' mentioned in %s is unknown to the driver",
    3441             :                          pszLine, pszFilename);
    3442             :             }
    3443         151 :             continue;
    3444             :         }
    3445             : 
    3446        1923 :         if (STARTS_WITH(pszLine, "closed_ways_are_polygons="))
    3447             :         {
    3448          30 :             char **papszTokens2 = CSLTokenizeString2(
    3449             :                 pszLine + strlen("closed_ways_are_polygons="), ",", 0);
    3450          30 :             m_nMinSizeKeysInSetClosedWaysArePolygons = INT_MAX;
    3451          30 :             m_nMaxSizeKeysInSetClosedWaysArePolygons = 0;
    3452         568 :             for (int i = 0; papszTokens2[i] != nullptr; i++)
    3453             :             {
    3454         538 :                 const int nTokenSize =
    3455         538 :                     static_cast<int>(strlen(papszTokens2[i]));
    3456         538 :                 aoSetClosedWaysArePolygons.insert(papszTokens2[i]);
    3457         538 :                 m_nMinSizeKeysInSetClosedWaysArePolygons = std::min(
    3458         538 :                     m_nMinSizeKeysInSetClosedWaysArePolygons, nTokenSize);
    3459         538 :                 m_nMaxSizeKeysInSetClosedWaysArePolygons = std::max(
    3460         538 :                     m_nMaxSizeKeysInSetClosedWaysArePolygons, nTokenSize);
    3461             :             }
    3462          30 :             CSLDestroy(papszTokens2);
    3463             :         }
    3464             : 
    3465        1893 :         else if (STARTS_WITH(pszLine, "report_all_tags="))
    3466             :         {
    3467           0 :             if (strcmp(pszLine + strlen("report_all_tags="), "yes") == 0)
    3468             :             {
    3469           0 :                 std::fill(begin(m_ignoredKeys), end(m_ignoredKeys), "");
    3470             :             }
    3471             :         }
    3472             : 
    3473        1893 :         else if (STARTS_WITH(pszLine, "report_all_nodes="))
    3474             :         {
    3475           0 :             if (strcmp(pszLine + strlen("report_all_nodes="), "no") == 0)
    3476             :             {
    3477           0 :                 m_bReportAllNodes = false;
    3478             :             }
    3479           0 :             else if (strcmp(pszLine + strlen("report_all_nodes="), "yes") == 0)
    3480             :             {
    3481           0 :                 m_bReportAllNodes = true;
    3482             :             }
    3483             :         }
    3484             : 
    3485        1893 :         else if (STARTS_WITH(pszLine, "report_all_ways="))
    3486             :         {
    3487           0 :             if (strcmp(pszLine + strlen("report_all_ways="), "no") == 0)
    3488             :             {
    3489           0 :                 m_bReportAllWays = false;
    3490             :             }
    3491           0 :             else if (strcmp(pszLine + strlen("report_all_ways="), "yes") == 0)
    3492             :             {
    3493           0 :                 m_bReportAllWays = true;
    3494             :             }
    3495             :         }
    3496             : 
    3497        1893 :         else if (STARTS_WITH(pszLine, "attribute_name_laundering="))
    3498             :         {
    3499           2 :             if (strcmp(pszLine + strlen("attribute_name_laundering="), "no") ==
    3500             :                 0)
    3501             :             {
    3502           0 :                 m_bAttributeNameLaundering = false;
    3503             :             }
    3504           2 :             else if (strcmp(pszLine + strlen("attribute_name_laundering="),
    3505             :                             "yes") == 0)
    3506             :             {
    3507           2 :                 m_bAttributeNameLaundering = true;
    3508             :             }
    3509             :         }
    3510             : 
    3511        1891 :         else if (STARTS_WITH(pszLine, "tags_format="))
    3512             :         {
    3513           0 :             if (EQUAL(pszLine + strlen("tags_format="), "json"))
    3514             :             {
    3515           0 :                 m_bTagsAsHSTORE = false;
    3516             :             }
    3517           0 :             else if (EQUAL(pszLine + strlen("tags_format="), "hstore"))
    3518             :             {
    3519           0 :                 m_bTagsAsHSTORE = true;
    3520             :             }
    3521             :             else
    3522             :             {
    3523           0 :                 CPLError(CE_Warning, CPLE_NotSupported,
    3524             :                          "Unsupported value for tags_format: %s",
    3525             :                          pszLine + strlen("tags_format="));
    3526             :             }
    3527             :         }
    3528             : 
    3529        1891 :         else if (iCurLayer >= 0)
    3530             :         {
    3531        1682 :             char **papszTokens = CSLTokenizeString2(pszLine, "=", 0);
    3532        3005 :             if (CSLCount(papszTokens) == 2 &&
    3533        1323 :                 strcmp(papszTokens[0], "other_tags") == 0)
    3534             :             {
    3535           0 :                 if (strcmp(papszTokens[1], "no") == 0)
    3536           0 :                     m_papoLayers[iCurLayer]->SetHasOtherTags(false);
    3537           0 :                 else if (strcmp(papszTokens[1], "yes") == 0)
    3538           0 :                     m_papoLayers[iCurLayer]->SetHasOtherTags(true);
    3539             :             }
    3540        3005 :             else if (CSLCount(papszTokens) == 2 &&
    3541        1323 :                      strcmp(papszTokens[0], "all_tags") == 0)
    3542             :             {
    3543           2 :                 if (strcmp(papszTokens[1], "no") == 0)
    3544           0 :                     m_papoLayers[iCurLayer]->SetHasAllTags(false);
    3545           2 :                 else if (strcmp(papszTokens[1], "yes") == 0)
    3546           2 :                     m_papoLayers[iCurLayer]->SetHasAllTags(true);
    3547             :             }
    3548        3001 :             else if (CSLCount(papszTokens) == 2 &&
    3549        1321 :                      strcmp(papszTokens[0], "osm_id") == 0)
    3550             :             {
    3551         150 :                 if (strcmp(papszTokens[1], "no") == 0)
    3552           0 :                     m_papoLayers[iCurLayer]->SetHasOSMId(false);
    3553         150 :                 else if (strcmp(papszTokens[1], "yes") == 0)
    3554             :                 {
    3555         150 :                     m_papoLayers[iCurLayer]->SetHasOSMId(true);
    3556         150 :                     m_papoLayers[iCurLayer]->AddField("osm_id", OFTString);
    3557             : 
    3558         150 :                     if (iCurLayer == IDX_LYR_MULTIPOLYGONS)
    3559          30 :                         m_papoLayers[iCurLayer]->AddField("osm_way_id",
    3560             :                                                           OFTString);
    3561             :                 }
    3562             :             }
    3563        2701 :             else if (CSLCount(papszTokens) == 2 &&
    3564        1171 :                      strcmp(papszTokens[0], "osm_version") == 0)
    3565             :             {
    3566         150 :                 if (strcmp(papszTokens[1], "no") == 0)
    3567         150 :                     m_papoLayers[iCurLayer]->SetHasVersion(false);
    3568           0 :                 else if (strcmp(papszTokens[1], "yes") == 0)
    3569             :                 {
    3570           0 :                     m_papoLayers[iCurLayer]->SetHasVersion(true);
    3571           0 :                     m_papoLayers[iCurLayer]->AddField("osm_version",
    3572             :                                                       OFTInteger);
    3573             :                 }
    3574             :             }
    3575        2401 :             else if (CSLCount(papszTokens) == 2 &&
    3576        1021 :                      strcmp(papszTokens[0], "osm_timestamp") == 0)
    3577             :             {
    3578         150 :                 if (strcmp(papszTokens[1], "no") == 0)
    3579         150 :                     m_papoLayers[iCurLayer]->SetHasTimestamp(false);
    3580           0 :                 else if (strcmp(papszTokens[1], "yes") == 0)
    3581             :                 {
    3582           0 :                     m_papoLayers[iCurLayer]->SetHasTimestamp(true);
    3583           0 :                     m_papoLayers[iCurLayer]->AddField("osm_timestamp",
    3584             :                                                       OFTDateTime);
    3585             :                 }
    3586             :             }
    3587        2101 :             else if (CSLCount(papszTokens) == 2 &&
    3588         871 :                      strcmp(papszTokens[0], "osm_uid") == 0)
    3589             :             {
    3590         150 :                 if (strcmp(papszTokens[1], "no") == 0)
    3591         150 :                     m_papoLayers[iCurLayer]->SetHasUID(false);
    3592           0 :                 else if (strcmp(papszTokens[1], "yes") == 0)
    3593             :                 {
    3594           0 :                     m_papoLayers[iCurLayer]->SetHasUID(true);
    3595           0 :                     m_papoLayers[iCurLayer]->AddField("osm_uid", OFTInteger);
    3596             :                 }
    3597             :             }
    3598        1801 :             else if (CSLCount(papszTokens) == 2 &&
    3599         721 :                      strcmp(papszTokens[0], "osm_user") == 0)
    3600             :             {
    3601         150 :                 if (strcmp(papszTokens[1], "no") == 0)
    3602         150 :                     m_papoLayers[iCurLayer]->SetHasUser(false);
    3603           0 :                 else if (strcmp(papszTokens[1], "yes") == 0)
    3604             :                 {
    3605           0 :                     m_papoLayers[iCurLayer]->SetHasUser(true);
    3606           0 :                     m_papoLayers[iCurLayer]->AddField("osm_user", OFTString);
    3607             :                 }
    3608             :             }
    3609        1501 :             else if (CSLCount(papszTokens) == 2 &&
    3610         571 :                      strcmp(papszTokens[0], "osm_changeset") == 0)
    3611             :             {
    3612         150 :                 if (strcmp(papszTokens[1], "no") == 0)
    3613         150 :                     m_papoLayers[iCurLayer]->SetHasChangeset(false);
    3614           0 :                 else if (strcmp(papszTokens[1], "yes") == 0)
    3615             :                 {
    3616           0 :                     m_papoLayers[iCurLayer]->SetHasChangeset(true);
    3617           0 :                     m_papoLayers[iCurLayer]->AddField("osm_changeset",
    3618             :                                                       OFTInteger);
    3619             :                 }
    3620             :             }
    3621        1201 :             else if (CSLCount(papszTokens) == 2 &&
    3622         421 :                      strcmp(papszTokens[0], "attributes") == 0)
    3623             :             {
    3624             :                 char **papszTokens2 =
    3625         151 :                     CSLTokenizeString2(papszTokens[1], ",", 0);
    3626        1382 :                 for (int i = 0; papszTokens2[i] != nullptr; i++)
    3627             :                 {
    3628        1231 :                     m_papoLayers[iCurLayer]->AddField(papszTokens2[i],
    3629             :                                                       OFTString);
    3630        9848 :                     for (const char *&pszIgnoredKey : m_ignoredKeys)
    3631             :                     {
    3632        8617 :                         if (strcmp(papszTokens2[i], pszIgnoredKey) == 0)
    3633           0 :                             pszIgnoredKey = "";
    3634             :                     }
    3635             :                 }
    3636         151 :                 CSLDestroy(papszTokens2);
    3637             :             }
    3638         899 :             else if (CSLCount(papszTokens) == 2 &&
    3639         270 :                      (strcmp(papszTokens[0], "unsignificant") == 0 ||
    3640         240 :                       strcmp(papszTokens[0], "insignificant") == 0))
    3641             :             {
    3642             :                 char **papszTokens2 =
    3643          30 :                     CSLTokenizeString2(papszTokens[1], ",", 0);
    3644         209 :                 for (int i = 0; papszTokens2[i] != nullptr; i++)
    3645             :                 {
    3646         179 :                     m_papoLayers[iCurLayer]->AddInsignificantKey(
    3647         179 :                         papszTokens2[i]);
    3648             :                 }
    3649          30 :                 CSLDestroy(papszTokens2);
    3650             :             }
    3651         839 :             else if (CSLCount(papszTokens) == 2 &&
    3652         240 :                      strcmp(papszTokens[0], "ignore") == 0)
    3653             :             {
    3654             :                 char **papszTokens2 =
    3655         150 :                     CSLTokenizeString2(papszTokens[1], ",", 0);
    3656        1735 :                 for (int i = 0; papszTokens2[i] != nullptr; i++)
    3657             :                 {
    3658        1585 :                     m_papoLayers[iCurLayer]->AddIgnoreKey(papszTokens2[i]);
    3659        1585 :                     m_papoLayers[iCurLayer]->AddWarnKey(papszTokens2[i]);
    3660             :                 }
    3661         150 :                 CSLDestroy(papszTokens2);
    3662             :             }
    3663         539 :             else if (CSLCount(papszTokens) == 2 &&
    3664          90 :                      strcmp(papszTokens[0], "computed_attributes") == 0)
    3665             :             {
    3666             :                 char **papszTokens2 =
    3667          30 :                     CSLTokenizeString2(papszTokens[1], ",", 0);
    3668          30 :                 oAttributes.resize(0);
    3669          60 :                 for (int i = 0; papszTokens2[i] != nullptr; i++)
    3670             :                 {
    3671          30 :                     oAttributes.push_back(
    3672          60 :                         OGROSMComputedAttribute(papszTokens2[i]));
    3673             :                 }
    3674          30 :                 CSLDestroy(papszTokens2);
    3675             :             }
    3676         419 :             else if (CSLCount(papszTokens) == 2 &&
    3677         479 :                      strlen(papszTokens[0]) >= 5 &&
    3678          60 :                      strcmp(papszTokens[0] + strlen(papszTokens[0]) - 5,
    3679             :                             "_type") == 0)
    3680             :             {
    3681          60 :                 CPLString osName(papszTokens[0]);
    3682          30 :                 osName.resize(strlen(papszTokens[0]) - 5);
    3683          30 :                 const char *pszType = papszTokens[1];
    3684          30 :                 bool bFound = false;
    3685          30 :                 OGRFieldType eType = OFTString;
    3686          30 :                 if (EQUAL(pszType, "Integer"))
    3687          30 :                     eType = OFTInteger;
    3688           0 :                 else if (EQUAL(pszType, "Integer64"))
    3689           0 :                     eType = OFTInteger64;
    3690           0 :                 else if (EQUAL(pszType, "Real"))
    3691           0 :                     eType = OFTReal;
    3692           0 :                 else if (EQUAL(pszType, "String"))
    3693           0 :                     eType = OFTString;
    3694           0 :                 else if (EQUAL(pszType, "DateTime"))
    3695           0 :                     eType = OFTDateTime;
    3696             :                 else
    3697           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    3698             :                              "Unhandled type (%s) for attribute %s", pszType,
    3699             :                              osName.c_str());
    3700          30 :                 for (size_t i = 0; i < oAttributes.size(); i++)
    3701             :                 {
    3702          30 :                     if (oAttributes[i].osName == osName)
    3703             :                     {
    3704          30 :                         bFound = true;
    3705          30 :                         oAttributes[i].eType = eType;
    3706          30 :                         break;
    3707             :                     }
    3708             :                 }
    3709          30 :                 if (!bFound)
    3710             :                 {
    3711             :                     const int idx =
    3712           0 :                         m_papoLayers[iCurLayer]->GetLayerDefn()->GetFieldIndex(
    3713           0 :                             osName);
    3714           0 :                     if (idx >= 0)
    3715             :                     {
    3716           0 :                         m_papoLayers[iCurLayer]
    3717           0 :                             ->GetLayerDefn()
    3718           0 :                             ->GetFieldDefn(idx)
    3719           0 :                             ->SetType(eType);
    3720           0 :                         bFound = true;
    3721             :                     }
    3722             :                 }
    3723          30 :                 if (!bFound)
    3724             :                 {
    3725           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    3726             :                              "Undeclared attribute : %s", osName.c_str());
    3727             :                 }
    3728             :             }
    3729         389 :             else if (CSLCount(papszTokens) >= 2 &&
    3730         419 :                      strlen(papszTokens[0]) >= 4 &&
    3731          30 :                      strcmp(papszTokens[0] + strlen(papszTokens[0]) - 4,
    3732             :                             "_sql") == 0)
    3733             :             {
    3734          60 :                 CPLString osName(papszTokens[0]);
    3735          30 :                 osName.resize(strlen(papszTokens[0]) - 4);
    3736          30 :                 size_t i = 0;  // Used after for.
    3737          30 :                 for (; i < oAttributes.size(); i++)
    3738             :                 {
    3739          30 :                     if (oAttributes[i].osName == osName)
    3740             :                     {
    3741          30 :                         const char *pszSQL = strchr(pszLine, '=') + 1;
    3742          30 :                         while (*pszSQL == ' ')
    3743           0 :                             pszSQL++;
    3744          30 :                         bool bInQuotes = false;
    3745          30 :                         if (*pszSQL == '"')
    3746             :                         {
    3747          30 :                             bInQuotes = true;
    3748          30 :                             pszSQL++;
    3749             :                         }
    3750          30 :                         oAttributes[i].osSQL = pszSQL;
    3751          60 :                         if (bInQuotes && oAttributes[i].osSQL.size() > 1 &&
    3752          30 :                             oAttributes[i].osSQL.back() == '"')
    3753          60 :                             oAttributes[i].osSQL.resize(
    3754          30 :                                 oAttributes[i].osSQL.size() - 1);
    3755          30 :                         break;
    3756             :                     }
    3757             :                 }
    3758          30 :                 if (i == oAttributes.size())
    3759             :                 {
    3760           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    3761             :                              "Undeclared attribute : %s", osName.c_str());
    3762             :                 }
    3763             :             }
    3764        1682 :             CSLDestroy(papszTokens);
    3765             :         }
    3766             :     }
    3767             : 
    3768          31 :     if (iCurLayer >= 0)
    3769          31 :         AddComputedAttributes(iCurLayer, oAttributes);
    3770             : 
    3771          31 :     VSIFCloseL(fpConf);
    3772             : 
    3773          31 :     return true;
    3774             : }
    3775             : 
    3776             : /************************************************************************/
    3777             : /*                          MyResetReading()                            */
    3778             : /************************************************************************/
    3779             : 
    3780          61 : int OGROSMDataSource::MyResetReading()
    3781             : {
    3782          61 :     if (m_hDB == nullptr)
    3783           0 :         return FALSE;
    3784          61 :     if (m_bCustomIndexing && m_fpNodes == nullptr)
    3785           0 :         return FALSE;
    3786             : 
    3787          61 :     OSM_ResetReading(m_psParser);
    3788             : 
    3789          61 :     char *pszErrMsg = nullptr;
    3790             :     int rc =
    3791          61 :         sqlite3_exec(m_hDB, "DELETE FROM nodes", nullptr, nullptr, &pszErrMsg);
    3792          61 :     if (rc != SQLITE_OK)
    3793             :     {
    3794           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3795             :                  "Unable to DELETE FROM nodes : %s", pszErrMsg);
    3796           0 :         sqlite3_free(pszErrMsg);
    3797           0 :         return FALSE;
    3798             :     }
    3799             : 
    3800          61 :     rc = sqlite3_exec(m_hDB, "DELETE FROM ways", nullptr, nullptr, &pszErrMsg);
    3801          61 :     if (rc != SQLITE_OK)
    3802             :     {
    3803           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unable to DELETE FROM ways : %s",
    3804             :                  pszErrMsg);
    3805           0 :         sqlite3_free(pszErrMsg);
    3806           0 :         return FALSE;
    3807             :     }
    3808             : 
    3809          61 :     rc = sqlite3_exec(m_hDB, "DELETE FROM polygons_standalone", nullptr,
    3810             :                       nullptr, &pszErrMsg);
    3811          61 :     if (rc != SQLITE_OK)
    3812             :     {
    3813           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3814             :                  "Unable to DELETE FROM polygons_standalone : %s", pszErrMsg);
    3815           0 :         sqlite3_free(pszErrMsg);
    3816           0 :         return FALSE;
    3817             :     }
    3818          61 :     m_bHasRowInPolygonsStandalone = false;
    3819             : 
    3820          61 :     if (m_hSelectPolygonsStandaloneStmt != nullptr)
    3821          61 :         sqlite3_reset(m_hSelectPolygonsStandaloneStmt);
    3822             : 
    3823             :     {
    3824          61 :         for (int i = 0; i < m_nWayFeaturePairs; i++)
    3825             :         {
    3826           0 :             delete m_pasWayFeaturePairs[i].poFeature;
    3827             :         }
    3828          61 :         m_nWayFeaturePairs = 0;
    3829          61 :         m_nUnsortedReqIds = 0;
    3830          61 :         m_nReqIds = 0;
    3831          61 :         m_nAccumulatedTags = 0;
    3832          61 :         nNonRedundantKeysLen = 0;
    3833          61 :         nNonRedundantValuesLen = 0;
    3834             : 
    3835          77 :         for (int i = 1; i < static_cast<int>(m_asKeys.size()); i++)
    3836             :         {
    3837          16 :             KeyDesc *psKD = m_asKeys[i];
    3838          16 :             if (psKD)
    3839             :             {
    3840          16 :                 CPLFree(psKD->pszK);
    3841          48 :                 for (int j = 0; j < (int)psKD->asValues.size(); j++)
    3842          32 :                     CPLFree(psKD->asValues[j]);
    3843          16 :                 delete psKD;
    3844             :             }
    3845             :         }
    3846          61 :         m_asKeys.resize(1);  // keep guard to avoid index 0 to be used
    3847          61 :         m_aoMapIndexedKeys.clear();
    3848             :     }
    3849             : 
    3850          61 :     if (m_bCustomIndexing)
    3851             :     {
    3852          60 :         m_nPrevNodeId = -1;
    3853          60 :         m_nBucketOld = -1;
    3854          60 :         m_nOffInBucketReducedOld = -1;
    3855             : 
    3856          60 :         VSIFSeekL(m_fpNodes, 0, SEEK_SET);
    3857          60 :         VSIFTruncateL(m_fpNodes, 0);
    3858          60 :         m_nNodesFileSize = 0;
    3859             : 
    3860          60 :         memset(m_pabySector, 0, SECTOR_SIZE);
    3861             : 
    3862          60 :         std::map<int, Bucket>::iterator oIter = m_oMapBuckets.begin();
    3863         100 :         for (; oIter != m_oMapBuckets.end(); ++oIter)
    3864             :         {
    3865          40 :             Bucket *psBucket = &(oIter->second);
    3866          40 :             psBucket->nOff = -1;
    3867          40 :             if (m_bCompressNodes)
    3868             :             {
    3869           0 :                 if (psBucket->u.panSectorSize)
    3870           0 :                     memset(psBucket->u.panSectorSize, 0,
    3871             :                            BUCKET_SECTOR_SIZE_ARRAY_SIZE);
    3872             :             }
    3873             :             else
    3874             :             {
    3875          40 :                 if (psBucket->u.pabyBitmap)
    3876          40 :                     memset(psBucket->u.pabyBitmap, 0, BUCKET_BITMAP_SIZE);
    3877             :             }
    3878             :         }
    3879             :     }
    3880             : 
    3881         366 :     for (int i = 0; i < m_nLayers; i++)
    3882             :     {
    3883         305 :         m_papoLayers[i]->ForceResetReading();
    3884             :     }
    3885             : 
    3886          61 :     m_bStopParsing = false;
    3887          61 :     m_poCurrentLayer = nullptr;
    3888             : 
    3889          61 :     return TRUE;
    3890             : }
    3891             : 
    3892             : /************************************************************************/
    3893             : /*                             ResetReading()                           */
    3894             : /************************************************************************/
    3895             : 
    3896           3 : void OGROSMDataSource::ResetReading()
    3897             : {
    3898           3 :     MyResetReading();
    3899           3 : }
    3900             : 
    3901             : /************************************************************************/
    3902             : /*                           GetNextFeature()                           */
    3903             : /************************************************************************/
    3904             : 
    3905          54 : OGRFeature *OGROSMDataSource::GetNextFeature(OGRLayer **ppoBelongingLayer,
    3906             :                                              double *pdfProgressPct,
    3907             :                                              GDALProgressFunc pfnProgress,
    3908             :                                              void *pProgressData)
    3909             : {
    3910          54 :     m_bInterleavedReading = TRUE;
    3911             : 
    3912          54 :     if (m_poCurrentLayer == nullptr)
    3913             :     {
    3914          10 :         m_poCurrentLayer = m_papoLayers[0];
    3915             :     }
    3916          54 :     if (pdfProgressPct != nullptr || pfnProgress != nullptr)
    3917             :     {
    3918          12 :         if (m_nFileSize == FILESIZE_NOT_INIT)
    3919             :         {
    3920             :             VSIStatBufL sStat;
    3921           1 :             if (VSIStatL(m_pszName, &sStat) == 0)
    3922             :             {
    3923           1 :                 m_nFileSize = static_cast<GIntBig>(sStat.st_size);
    3924             :             }
    3925             :             else
    3926             :             {
    3927           0 :                 m_nFileSize = FILESIZE_INVALID;
    3928             :             }
    3929             :         }
    3930             :     }
    3931             : 
    3932             :     while (true)
    3933             :     {
    3934          75 :         OGROSMLayer *poNewCurLayer = nullptr;
    3935          75 :         CPLAssert(m_poCurrentLayer != nullptr);
    3936          75 :         OGRFeature *poFeature = m_poCurrentLayer->MyGetNextFeature(
    3937             :             &poNewCurLayer, pfnProgress, pProgressData);
    3938          75 :         m_poCurrentLayer = poNewCurLayer;
    3939          75 :         if (poFeature == nullptr)
    3940             :         {
    3941          29 :             if (m_poCurrentLayer != nullptr)
    3942          21 :                 continue;
    3943           8 :             if (ppoBelongingLayer != nullptr)
    3944           8 :                 *ppoBelongingLayer = nullptr;
    3945           8 :             if (pdfProgressPct != nullptr)
    3946           2 :                 *pdfProgressPct = 1.0;
    3947          54 :             return nullptr;
    3948             :         }
    3949          46 :         if (ppoBelongingLayer != nullptr)
    3950          46 :             *ppoBelongingLayer = m_poCurrentLayer;
    3951          46 :         if (pdfProgressPct != nullptr)
    3952             :         {
    3953           8 :             if (m_nFileSize != FILESIZE_INVALID)
    3954             :             {
    3955           8 :                 *pdfProgressPct =
    3956           8 :                     1.0 * OSM_GetBytesRead(m_psParser) / m_nFileSize;
    3957             :             }
    3958             :             else
    3959             :             {
    3960           0 :                 *pdfProgressPct = -1.0;
    3961             :             }
    3962             :         }
    3963             : 
    3964          46 :         return poFeature;
    3965          21 :     }
    3966             : }
    3967             : 
    3968             : /************************************************************************/
    3969             : /*                           ParseNextChunk()                           */
    3970             : /************************************************************************/
    3971             : 
    3972         108 : bool OGROSMDataSource::ParseNextChunk(int nIdxLayer,
    3973             :                                       GDALProgressFunc pfnProgress,
    3974             :                                       void *pProgressData)
    3975             : {
    3976         108 :     if (m_bStopParsing)
    3977          61 :         return false;
    3978             : 
    3979          47 :     m_bHasParsedFirstChunk = true;
    3980          47 :     m_bFeatureAdded = false;
    3981             :     while (true)
    3982             :     {
    3983             : #ifdef DEBUG_MEM_USAGE
    3984             :         static int counter = 0;
    3985             :         counter++;
    3986             :         if ((counter % 1000) == 0)
    3987             :             CPLDebug("OSM", "GetMaxTotalAllocs() = " CPL_FRMT_GUIB,
    3988             :                      static_cast<GUIntBig>(GetMaxTotalAllocs()));
    3989             : #endif
    3990             : 
    3991          81 :         OSMRetCode eRet = OSM_ProcessBlock(m_psParser);
    3992          81 :         if (pfnProgress != nullptr)
    3993             :         {
    3994           3 :             double dfPct = -1.0;
    3995           3 :             if (m_nFileSize != FILESIZE_INVALID)
    3996             :             {
    3997           3 :                 dfPct = 1.0 * OSM_GetBytesRead(m_psParser) / m_nFileSize;
    3998             :             }
    3999           3 :             if (!pfnProgress(dfPct, "", pProgressData))
    4000             :             {
    4001           1 :                 m_bStopParsing = true;
    4002           6 :                 for (int i = 0; i < m_nLayers; i++)
    4003             :                 {
    4004           5 :                     m_papoLayers[i]->ForceResetReading();
    4005             :                 }
    4006           1 :                 return false;
    4007             :             }
    4008             :         }
    4009             : 
    4010          80 :         if (eRet == OSM_EOF || eRet == OSM_ERROR)
    4011             :         {
    4012          44 :             if (eRet == OSM_EOF)
    4013             :             {
    4014          41 :                 if (m_nWayFeaturePairs != 0)
    4015           2 :                     ProcessWaysBatch();
    4016             : 
    4017          41 :                 ProcessPolygonsStandalone();
    4018             : 
    4019          41 :                 if (!m_bHasRowInPolygonsStandalone)
    4020          41 :                     m_bStopParsing = true;
    4021             : 
    4022          41 :                 if (!m_bInterleavedReading && !m_bFeatureAdded &&
    4023           6 :                     m_bHasRowInPolygonsStandalone &&
    4024             :                     nIdxLayer != IDX_LYR_MULTIPOLYGONS)
    4025             :                 {
    4026           0 :                     return false;
    4027             :                 }
    4028             : 
    4029          41 :                 return m_bFeatureAdded || m_bHasRowInPolygonsStandalone;
    4030             :             }
    4031             :             else
    4032             :             {
    4033           3 :                 CPLError(CE_Failure, CPLE_AppDefined,
    4034             :                          "An error occurred during the parsing of data "
    4035             :                          "around byte " CPL_FRMT_GUIB,
    4036             :                          OSM_GetBytesRead(m_psParser));
    4037             : 
    4038           3 :                 m_bStopParsing = true;
    4039           3 :                 return false;
    4040             :             }
    4041             :         }
    4042             :         else
    4043             :         {
    4044          36 :             if (m_bInMemoryTmpDB)
    4045             :             {
    4046          36 :                 if (!TransferToDiskIfNecesserary())
    4047           0 :                     return false;
    4048             :             }
    4049             : 
    4050          36 :             if (m_bFeatureAdded)
    4051           2 :                 break;
    4052             :         }
    4053          34 :     }
    4054             : 
    4055           2 :     return true;
    4056             : }
    4057             : 
    4058             : /************************************************************************/
    4059             : /*                    TransferToDiskIfNecesserary()                     */
    4060             : /************************************************************************/
    4061             : 
    4062          36 : bool OGROSMDataSource::TransferToDiskIfNecesserary()
    4063             : {
    4064          36 :     if (m_bInMemoryNodesFile)
    4065             :     {
    4066          34 :         if (m_nNodesFileSize / 1024 / 1024 >
    4067          34 :             3 * m_nMaxSizeForInMemoryDBInMB / 4)
    4068             :         {
    4069           0 :             m_bInMemoryNodesFile = false;
    4070             : 
    4071           0 :             VSIFCloseL(m_fpNodes);
    4072           0 :             m_fpNodes = nullptr;
    4073             : 
    4074             :             const std::string osNewTmpDBName(
    4075           0 :                 CPLGenerateTempFilename("osm_tmp_nodes"));
    4076             : 
    4077           0 :             CPLDebug("OSM",
    4078             :                      "%s too big for RAM. Transferring it onto disk in %s",
    4079             :                      m_osNodesFilename.c_str(), osNewTmpDBName.c_str());
    4080             : 
    4081           0 :             if (CPLCopyFile(osNewTmpDBName.c_str(), m_osNodesFilename) != 0)
    4082             :             {
    4083           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Cannot copy %s to %s",
    4084             :                          m_osNodesFilename.c_str(), osNewTmpDBName.c_str());
    4085           0 :                 VSIUnlink(osNewTmpDBName.c_str());
    4086           0 :                 m_bStopParsing = true;
    4087           0 :                 return false;
    4088             :             }
    4089             : 
    4090           0 :             VSIUnlink(m_osNodesFilename);
    4091             : 
    4092           0 :             if (m_bInMemoryTmpDB)
    4093             :             {
    4094             :                 /* Try to grow the sqlite in memory-db to the full space now */
    4095             :                 /* it has been freed. */
    4096           0 :                 VSILFILE *fp = VSIFOpenL(m_osTmpDBName, "rb+");
    4097           0 :                 if (fp)
    4098             :                 {
    4099           0 :                     VSIFSeekL(fp, 0, SEEK_END);
    4100           0 :                     vsi_l_offset nCurSize = VSIFTellL(fp);
    4101           0 :                     GIntBig nNewSize =
    4102           0 :                         static_cast<GIntBig>(m_nMaxSizeForInMemoryDBInMB) *
    4103             :                         1024 * 1024;
    4104           0 :                     CPLPushErrorHandler(CPLQuietErrorHandler);
    4105             :                     const bool bSuccess =
    4106           0 :                         VSIFSeekL(fp, (vsi_l_offset)nNewSize, SEEK_SET) == 0;
    4107           0 :                     CPLPopErrorHandler();
    4108             : 
    4109           0 :                     if (bSuccess)
    4110           0 :                         VSIFTruncateL(fp, nCurSize);
    4111             : 
    4112           0 :                     VSIFCloseL(fp);
    4113             :                 }
    4114             :             }
    4115             : 
    4116           0 :             m_osNodesFilename = osNewTmpDBName;
    4117             : 
    4118           0 :             m_fpNodes = VSIFOpenL(m_osNodesFilename, "rb+");
    4119           0 :             if (m_fpNodes == nullptr)
    4120             :             {
    4121           0 :                 m_bStopParsing = true;
    4122           0 :                 return false;
    4123             :             }
    4124             : 
    4125           0 :             VSIFSeekL(m_fpNodes, 0, SEEK_END);
    4126             : 
    4127             :             /* On Unix filesystems, you can remove a file even if it */
    4128             :             /* opened */
    4129             :             const char *pszVal =
    4130           0 :                 CPLGetConfigOption("OSM_UNLINK_TMPFILE", "YES");
    4131           0 :             if (EQUAL(pszVal, "YES"))
    4132             :             {
    4133           0 :                 CPLPushErrorHandler(CPLQuietErrorHandler);
    4134           0 :                 m_bMustUnlinkNodesFile = VSIUnlink(m_osNodesFilename) != 0;
    4135           0 :                 CPLPopErrorHandler();
    4136             :             }
    4137             :         }
    4138             :     }
    4139             : 
    4140          36 :     if (m_bInMemoryTmpDB)
    4141             :     {
    4142             :         VSIStatBufL sStat;
    4143             : 
    4144          36 :         int nLimitMB = m_nMaxSizeForInMemoryDBInMB;
    4145          36 :         if (m_bCustomIndexing && m_bInMemoryNodesFile)
    4146          34 :             nLimitMB = nLimitMB * 1 / 4;
    4147             : 
    4148          72 :         if (VSIStatL(m_osTmpDBName, &sStat) == 0 &&
    4149          36 :             sStat.st_size / 1024 / 1024 > nLimitMB)
    4150             :         {
    4151           0 :             m_bInMemoryTmpDB = false;
    4152             : 
    4153           0 :             CloseDB();
    4154             : 
    4155             :             const std::string osNewTmpDBName(
    4156           0 :                 CPLGenerateTempFilename("osm_tmp"));
    4157             : 
    4158           0 :             CPLDebug("OSM",
    4159             :                      "%s too big for RAM. Transferring it onto disk in %s",
    4160             :                      m_osTmpDBName.c_str(), osNewTmpDBName.c_str());
    4161             : 
    4162           0 :             if (CPLCopyFile(osNewTmpDBName.c_str(), m_osTmpDBName) != 0)
    4163             :             {
    4164           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Cannot copy %s to %s",
    4165             :                          m_osTmpDBName.c_str(), osNewTmpDBName.c_str());
    4166           0 :                 VSIUnlink(osNewTmpDBName.c_str());
    4167           0 :                 m_bStopParsing = true;
    4168           0 :                 return false;
    4169             :             }
    4170             : 
    4171           0 :             VSIUnlink(m_osTmpDBName);
    4172             : 
    4173           0 :             m_osTmpDBName = osNewTmpDBName;
    4174             : 
    4175           0 :             const int rc = sqlite3_open_v2(
    4176             :                 m_osTmpDBName.c_str(), &m_hDB,
    4177             :                 SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX, nullptr);
    4178           0 :             if (rc != SQLITE_OK)
    4179             :             {
    4180           0 :                 CPLError(CE_Failure, CPLE_OpenFailed,
    4181             :                          "sqlite3_open(%s) failed: %s", m_osTmpDBName.c_str(),
    4182             :                          sqlite3_errmsg(m_hDB));
    4183           0 :                 m_bStopParsing = true;
    4184           0 :                 CloseDB();
    4185           0 :                 return false;
    4186             :             }
    4187             : 
    4188             :             /* On Unix filesystems, you can remove a file even if it */
    4189             :             /* opened */
    4190             :             const char *pszVal =
    4191           0 :                 CPLGetConfigOption("OSM_UNLINK_TMPFILE", "YES");
    4192           0 :             if (EQUAL(pszVal, "YES"))
    4193             :             {
    4194           0 :                 CPLPushErrorHandler(CPLQuietErrorHandler);
    4195           0 :                 m_bMustUnlink = VSIUnlink(m_osTmpDBName) != 0;
    4196           0 :                 CPLPopErrorHandler();
    4197             :             }
    4198             : 
    4199           0 :             if (!SetDBOptions() || !CreatePreparedStatements())
    4200             :             {
    4201           0 :                 m_bStopParsing = true;
    4202           0 :                 CloseDB();
    4203           0 :                 return false;
    4204             :             }
    4205             :         }
    4206             :     }
    4207             : 
    4208          36 :     return true;
    4209             : }
    4210             : 
    4211             : /************************************************************************/
    4212             : /*                           TestCapability()                           */
    4213             : /************************************************************************/
    4214             : 
    4215           7 : int OGROSMDataSource::TestCapability(const char *pszCap)
    4216             : {
    4217           7 :     return EQUAL(pszCap, ODsCRandomLayerRead);
    4218             : }
    4219             : 
    4220             : /************************************************************************/
    4221             : /*                              GetLayer()                              */
    4222             : /************************************************************************/
    4223             : 
    4224         426 : OGRLayer *OGROSMDataSource::GetLayer(int iLayer)
    4225             : 
    4226             : {
    4227         426 :     if (iLayer < 0 || iLayer >= m_nLayers)
    4228           0 :         return nullptr;
    4229             : 
    4230         426 :     return m_papoLayers[iLayer];
    4231             : }
    4232             : 
    4233             : /************************************************************************/
    4234             : /*                             GetExtent()                              */
    4235             : /************************************************************************/
    4236             : 
    4237           3 : OGRErr OGROSMDataSource::GetExtent(OGREnvelope *psExtent)
    4238             : {
    4239           3 :     if (!m_bHasParsedFirstChunk)
    4240             :     {
    4241           3 :         m_bHasParsedFirstChunk = true;
    4242           3 :         OSM_ProcessBlock(m_psParser);
    4243             :     }
    4244             : 
    4245           3 :     if (m_bExtentValid)
    4246             :     {
    4247           3 :         *psExtent = m_sExtent;
    4248           3 :         return OGRERR_NONE;
    4249             :     }
    4250             : 
    4251           0 :     return OGRERR_FAILURE;
    4252             : }
    4253             : 
    4254             : /************************************************************************/
    4255             : /*                   OGROSMSingleFeatureLayer                           */
    4256             : /************************************************************************/
    4257             : 
    4258             : class OGROSMSingleFeatureLayer final : public OGRLayer
    4259             : {
    4260             :   private:
    4261             :     int nVal;
    4262             :     char *pszVal;
    4263             :     OGRFeatureDefn *poFeatureDefn;
    4264             :     int iNextShapeId;
    4265             : 
    4266             :   public:
    4267             :     OGROSMSingleFeatureLayer(const char *pszLayerName, int nVal);
    4268             :     OGROSMSingleFeatureLayer(const char *pszLayerName, const char *pszVal);
    4269             :     virtual ~OGROSMSingleFeatureLayer();
    4270             : 
    4271           6 :     virtual void ResetReading() override
    4272             :     {
    4273           6 :         iNextShapeId = 0;
    4274           6 :     }
    4275             : 
    4276             :     virtual OGRFeature *GetNextFeature() override;
    4277             : 
    4278           6 :     virtual OGRFeatureDefn *GetLayerDefn() override
    4279             :     {
    4280           6 :         return poFeatureDefn;
    4281             :     }
    4282             : 
    4283           6 :     virtual int TestCapability(const char *) override
    4284             :     {
    4285           6 :         return FALSE;
    4286             :     }
    4287             : };
    4288             : 
    4289             : /************************************************************************/
    4290             : /*                    OGROSMSingleFeatureLayer()                        */
    4291             : /************************************************************************/
    4292             : 
    4293           0 : OGROSMSingleFeatureLayer::OGROSMSingleFeatureLayer(const char *pszLayerName,
    4294           0 :                                                    int nValIn)
    4295             :     : nVal(nValIn), pszVal(nullptr),
    4296           0 :       poFeatureDefn(new OGRFeatureDefn("SELECT")), iNextShapeId(0)
    4297             : {
    4298           0 :     poFeatureDefn->Reference();
    4299           0 :     OGRFieldDefn oField(pszLayerName, OFTInteger);
    4300           0 :     poFeatureDefn->AddFieldDefn(&oField);
    4301           0 : }
    4302             : 
    4303             : /************************************************************************/
    4304             : /*                    OGROSMSingleFeatureLayer()                        */
    4305             : /************************************************************************/
    4306             : 
    4307           6 : OGROSMSingleFeatureLayer::OGROSMSingleFeatureLayer(const char *pszLayerName,
    4308           6 :                                                    const char *pszValIn)
    4309          12 :     : nVal(0), pszVal(CPLStrdup(pszValIn)),
    4310           6 :       poFeatureDefn(new OGRFeatureDefn("SELECT")), iNextShapeId(0)
    4311             : {
    4312           6 :     poFeatureDefn->Reference();
    4313          12 :     OGRFieldDefn oField(pszLayerName, OFTString);
    4314           6 :     poFeatureDefn->AddFieldDefn(&oField);
    4315           6 : }
    4316             : 
    4317             : /************************************************************************/
    4318             : /*                    ~OGROSMSingleFeatureLayer()                       */
    4319             : /************************************************************************/
    4320             : 
    4321          12 : OGROSMSingleFeatureLayer::~OGROSMSingleFeatureLayer()
    4322             : {
    4323           6 :     poFeatureDefn->Release();
    4324           6 :     CPLFree(pszVal);
    4325          12 : }
    4326             : 
    4327             : /************************************************************************/
    4328             : /*                           GetNextFeature()                           */
    4329             : /************************************************************************/
    4330             : 
    4331          18 : OGRFeature *OGROSMSingleFeatureLayer::GetNextFeature()
    4332             : {
    4333          18 :     if (iNextShapeId != 0)
    4334           6 :         return nullptr;
    4335             : 
    4336          12 :     OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
    4337          12 :     if (pszVal)
    4338          12 :         poFeature->SetField(0, pszVal);
    4339             :     else
    4340           0 :         poFeature->SetField(0, nVal);
    4341          12 :     poFeature->SetFID(iNextShapeId++);
    4342          12 :     return poFeature;
    4343             : }
    4344             : 
    4345             : /************************************************************************/
    4346             : /*                      OGROSMResultLayerDecorator                      */
    4347             : /************************************************************************/
    4348             : 
    4349             : class OGROSMResultLayerDecorator final : public OGRLayerDecorator
    4350             : {
    4351             :     std::string osDSName;
    4352             :     std::string osInterestLayers;
    4353             : 
    4354             :   public:
    4355           1 :     OGROSMResultLayerDecorator(OGRLayer *poLayer, const std::string &osDSNameIn,
    4356             :                                const std::string &osInterestLayersIn)
    4357           1 :         : OGRLayerDecorator(poLayer, TRUE), osDSName(osDSNameIn),
    4358           1 :           osInterestLayers(osInterestLayersIn)
    4359             :     {
    4360           1 :     }
    4361             : 
    4362           1 :     virtual GIntBig GetFeatureCount(int bForce = TRUE) override
    4363             :     {
    4364             :         /* When we run GetFeatureCount() with SQLite SQL dialect, */
    4365             :         /* the OSM dataset will be re-opened. Make sure that it is */
    4366             :         /* re-opened with the same interest layers */
    4367           1 :         AddInterestLayersForDSName(osDSName, osInterestLayers);
    4368           1 :         return OGRLayerDecorator::GetFeatureCount(bForce);
    4369             :     }
    4370             : };
    4371             : 
    4372             : /************************************************************************/
    4373             : /*                             ExecuteSQL()                             */
    4374             : /************************************************************************/
    4375             : 
    4376          40 : OGRLayer *OGROSMDataSource::ExecuteSQL(const char *pszSQLCommand,
    4377             :                                        OGRGeometry *poSpatialFilter,
    4378             :                                        const char *pszDialect)
    4379             : 
    4380             : {
    4381             :     /* -------------------------------------------------------------------- */
    4382             :     /*      Special GetBytesRead() command                                  */
    4383             :     /* -------------------------------------------------------------------- */
    4384          40 :     if (strcmp(pszSQLCommand, "GetBytesRead()") == 0)
    4385             :     {
    4386           6 :         char szVal[64] = {};
    4387           6 :         snprintf(szVal, sizeof(szVal), CPL_FRMT_GUIB,
    4388             :                  OSM_GetBytesRead(m_psParser));
    4389           6 :         return new OGROSMSingleFeatureLayer("GetBytesRead", szVal);
    4390             :     }
    4391             : 
    4392          34 :     if (m_poResultSetLayer != nullptr)
    4393             :     {
    4394           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    4395             :                  "A SQL result layer is still in use. Please delete it first");
    4396           0 :         return nullptr;
    4397             :     }
    4398             : 
    4399             :     /* -------------------------------------------------------------------- */
    4400             :     /*      Special SET interest_layers = command                           */
    4401             :     /* -------------------------------------------------------------------- */
    4402          34 :     if (STARTS_WITH(pszSQLCommand, "SET interest_layers ="))
    4403             :     {
    4404             :         char **papszTokens =
    4405          19 :             CSLTokenizeString2(pszSQLCommand + 21, ",",
    4406             :                                CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
    4407         114 :         for (int i = 0; i < m_nLayers; i++)
    4408             :         {
    4409          95 :             m_papoLayers[i]->SetDeclareInterest(FALSE);
    4410             :         }
    4411             : 
    4412          47 :         for (int i = 0; papszTokens[i] != nullptr; i++)
    4413             :         {
    4414             :             OGROSMLayer *poLayer =
    4415          28 :                 reinterpret_cast<OGROSMLayer *>(GetLayerByName(papszTokens[i]));
    4416          28 :             if (poLayer != nullptr)
    4417             :             {
    4418          28 :                 poLayer->SetDeclareInterest(TRUE);
    4419             :             }
    4420             :         }
    4421             : 
    4422          19 :         if (m_papoLayers[IDX_LYR_POINTS]->IsUserInterested() &&
    4423           9 :             !m_papoLayers[IDX_LYR_LINES]->IsUserInterested() &&
    4424           6 :             !m_papoLayers[IDX_LYR_MULTILINESTRINGS]->IsUserInterested() &&
    4425          34 :             !m_papoLayers[IDX_LYR_MULTIPOLYGONS]->IsUserInterested() &&
    4426           6 :             !m_papoLayers[IDX_LYR_OTHER_RELATIONS]->IsUserInterested())
    4427             :         {
    4428           6 :             if (CPLGetConfigOption("OSM_INDEX_POINTS", nullptr) == nullptr)
    4429             :             {
    4430           6 :                 CPLDebug("OSM", "Disabling indexing of nodes");
    4431           6 :                 m_bIndexPoints = false;
    4432             :             }
    4433           6 :             if (CPLGetConfigOption("OSM_USE_POINTS_INDEX", nullptr) == nullptr)
    4434             :             {
    4435           6 :                 m_bUsePointsIndex = false;
    4436             :             }
    4437           6 :             if (CPLGetConfigOption("OSM_INDEX_WAYS", nullptr) == nullptr)
    4438             :             {
    4439           6 :                 CPLDebug("OSM", "Disabling indexing of ways");
    4440           6 :                 m_bIndexWays = false;
    4441             :             }
    4442           6 :             if (CPLGetConfigOption("OSM_USE_WAYS_INDEX", nullptr) == nullptr)
    4443             :             {
    4444           6 :                 m_bUseWaysIndex = false;
    4445             :             }
    4446             :         }
    4447          13 :         else if (m_papoLayers[IDX_LYR_LINES]->IsUserInterested() &&
    4448           6 :                  !m_papoLayers[IDX_LYR_MULTILINESTRINGS]->IsUserInterested() &&
    4449          22 :                  !m_papoLayers[IDX_LYR_MULTIPOLYGONS]->IsUserInterested() &&
    4450           3 :                  !m_papoLayers[IDX_LYR_OTHER_RELATIONS]->IsUserInterested())
    4451             :         {
    4452           3 :             if (CPLGetConfigOption("OSM_INDEX_WAYS", nullptr) == nullptr)
    4453             :             {
    4454           3 :                 CPLDebug("OSM", "Disabling indexing of ways");
    4455           3 :                 m_bIndexWays = false;
    4456             :             }
    4457           3 :             if (CPLGetConfigOption("OSM_USE_WAYS_INDEX", nullptr) == nullptr)
    4458             :             {
    4459           3 :                 m_bUseWaysIndex = false;
    4460             :             }
    4461             :         }
    4462             : 
    4463          19 :         CSLDestroy(papszTokens);
    4464             : 
    4465          19 :         return nullptr;
    4466             :     }
    4467             : 
    4468          15 :     while (*pszSQLCommand == ' ')
    4469           0 :         pszSQLCommand++;
    4470             : 
    4471             :     /* Try to analyse the SQL command to get the interest table */
    4472          15 :     if (STARTS_WITH_CI(pszSQLCommand, "SELECT"))
    4473             :     {
    4474          15 :         bool bLayerAlreadyAdded = false;
    4475          15 :         CPLString osInterestLayers = "SET interest_layers =";
    4476             : 
    4477          15 :         if (pszDialect != nullptr && EQUAL(pszDialect, "SQLITE"))
    4478             :         {
    4479             :             std::set<LayerDesc> oSetLayers =
    4480           2 :                 OGRSQLiteGetReferencedLayers(pszSQLCommand);
    4481           1 :             std::set<LayerDesc>::iterator oIter = oSetLayers.begin();
    4482           2 :             for (; oIter != oSetLayers.end(); ++oIter)
    4483             :             {
    4484           1 :                 const LayerDesc &oLayerDesc = *oIter;
    4485           1 :                 if (oLayerDesc.osDSName.empty())
    4486             :                 {
    4487           1 :                     if (bLayerAlreadyAdded)
    4488           0 :                         osInterestLayers += ",";
    4489           1 :                     bLayerAlreadyAdded = true;
    4490           1 :                     osInterestLayers += oLayerDesc.osLayerName;
    4491             :                 }
    4492           1 :             }
    4493             :         }
    4494             :         else
    4495             :         {
    4496          28 :             swq_select sSelectInfo;
    4497             : 
    4498          14 :             CPLPushErrorHandler(CPLQuietErrorHandler);
    4499          14 :             CPLErr eErr = sSelectInfo.preparse(pszSQLCommand);
    4500          14 :             CPLPopErrorHandler();
    4501             : 
    4502          14 :             if (eErr == CE_None)
    4503             :             {
    4504          14 :                 swq_select *pCurSelect = &sSelectInfo;
    4505          28 :                 while (pCurSelect != nullptr)
    4506             :                 {
    4507          28 :                     for (int iTable = 0; iTable < pCurSelect->table_count;
    4508             :                          iTable++)
    4509             :                     {
    4510          14 :                         swq_table_def *psTableDef =
    4511          14 :                             pCurSelect->table_defs + iTable;
    4512          14 :                         if (psTableDef->data_source == nullptr)
    4513             :                         {
    4514          14 :                             if (bLayerAlreadyAdded)
    4515           0 :                                 osInterestLayers += ",";
    4516          14 :                             bLayerAlreadyAdded = true;
    4517          14 :                             osInterestLayers += psTableDef->table_name;
    4518             :                         }
    4519             :                     }
    4520          14 :                     pCurSelect = pCurSelect->poOtherSelect;
    4521             :                 }
    4522             :             }
    4523             :         }
    4524             : 
    4525          15 :         if (bLayerAlreadyAdded)
    4526             :         {
    4527             :             /* Backup current optimization parameters */
    4528          15 :             m_abSavedDeclaredInterest.resize(0);
    4529          90 :             for (int i = 0; i < m_nLayers; i++)
    4530             :             {
    4531          75 :                 m_abSavedDeclaredInterest.push_back(
    4532          75 :                     m_papoLayers[i]->IsUserInterested());
    4533             :             }
    4534          15 :             m_bIndexPointsBackup = m_bIndexPoints;
    4535          15 :             m_bUsePointsIndexBackup = m_bUsePointsIndex;
    4536          15 :             m_bIndexWaysBackup = m_bIndexWays;
    4537          15 :             m_bUseWaysIndexBackup = m_bUseWaysIndex;
    4538             : 
    4539             :             /* Update optimization parameters */
    4540          15 :             delete ExecuteSQL(osInterestLayers, nullptr, nullptr);
    4541             : 
    4542          15 :             MyResetReading();
    4543             : 
    4544             :             /* Run the request */
    4545          15 :             m_poResultSetLayer = OGRDataSource::ExecuteSQL(
    4546             :                 pszSQLCommand, poSpatialFilter, pszDialect);
    4547             : 
    4548             :             /* If the user explicitly run a COUNT() request, then do it ! */
    4549          15 :             if (m_poResultSetLayer)
    4550             :             {
    4551          15 :                 if (pszDialect != nullptr && EQUAL(pszDialect, "SQLITE"))
    4552             :                 {
    4553           2 :                     m_poResultSetLayer = new OGROSMResultLayerDecorator(
    4554           1 :                         m_poResultSetLayer, GetName(), osInterestLayers);
    4555             :                 }
    4556          15 :                 m_bIsFeatureCountEnabled = true;
    4557             :             }
    4558             : 
    4559          15 :             return m_poResultSetLayer;
    4560             :         }
    4561             :     }
    4562             : 
    4563           0 :     return OGRDataSource::ExecuteSQL(pszSQLCommand, poSpatialFilter,
    4564           0 :                                      pszDialect);
    4565             : }
    4566             : 
    4567             : /************************************************************************/
    4568             : /*                          ReleaseResultSet()                          */
    4569             : /************************************************************************/
    4570             : 
    4571          21 : void OGROSMDataSource::ReleaseResultSet(OGRLayer *poLayer)
    4572             : 
    4573             : {
    4574          21 :     if (poLayer != nullptr && poLayer == m_poResultSetLayer)
    4575             :     {
    4576          15 :         m_poResultSetLayer = nullptr;
    4577             : 
    4578          15 :         m_bIsFeatureCountEnabled = false;
    4579             : 
    4580             :         /* Restore backup'ed optimization parameters */
    4581          90 :         for (int i = 0; i < m_nLayers; i++)
    4582             :         {
    4583          75 :             m_papoLayers[i]->SetDeclareInterest(m_abSavedDeclaredInterest[i]);
    4584             :         }
    4585          15 :         if (m_bIndexPointsBackup && !m_bIndexPoints)
    4586           5 :             CPLDebug("OSM", "Re-enabling indexing of nodes");
    4587          15 :         m_bIndexPoints = m_bIndexPointsBackup;
    4588          15 :         m_bUsePointsIndex = m_bUsePointsIndexBackup;
    4589          15 :         if (m_bIndexWaysBackup && !m_bIndexWays)
    4590           8 :             CPLDebug("OSM", "Re-enabling indexing of ways");
    4591          15 :         m_bIndexWays = m_bIndexWaysBackup;
    4592          15 :         m_bUseWaysIndex = m_bUseWaysIndexBackup;
    4593          15 :         m_abSavedDeclaredInterest.resize(0);
    4594             :     }
    4595             : 
    4596          21 :     delete poLayer;
    4597          21 : }
    4598             : 
    4599             : /************************************************************************/
    4600             : /*                         IsInterleavedReading()                       */
    4601             : /************************************************************************/
    4602             : 
    4603         147 : int OGROSMDataSource::IsInterleavedReading()
    4604             : {
    4605         147 :     if (m_bInterleavedReading < 0)
    4606             :     {
    4607          24 :         m_bInterleavedReading =
    4608          24 :             CPLTestBool(CPLGetConfigOption("OGR_INTERLEAVED_READING", "NO"));
    4609          24 :         CPLDebug("OSM", "OGR_INTERLEAVED_READING = %d", m_bInterleavedReading);
    4610             :     }
    4611         147 :     return m_bInterleavedReading;
    4612             : }

Generated by: LCOV version 1.14