LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/osm - ogrosmdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1755 2235 78.5 %
Date: 2024-11-21 22:18:42 Functions: 78 80 97.5 %

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

Generated by: LCOV version 1.14