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

Generated by: LCOV version 1.14