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

Generated by: LCOV version 1.14