LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/osm - ogrosmlayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 396 491 80.7 %
Date: 2024-05-07 17:03:27 Functions: 26 27 96.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements OGROSMLayer class
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2012-2014, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #include "cpl_port.h"
      30             : 
      31             : #include <cstddef>
      32             : #include <cstdio>
      33             : #include <cstdlib>
      34             : #include <cstring>
      35             : #include <time.h>
      36             : #include <map>
      37             : #include <memory>
      38             : #include <set>
      39             : #include <string>
      40             : #include <utility>
      41             : #include <vector>
      42             : 
      43             : #include "cpl_conv.h"
      44             : #include "cpl_error.h"
      45             : #include "cpl_progress.h"
      46             : #include "cpl_string.h"
      47             : #include "cpl_time.h"
      48             : #include "cpl_vsi.h"
      49             : #include "ogr_core.h"
      50             : #include "ogr_feature.h"
      51             : #include "ogr_geometry.h"
      52             : #include "ogr_p.h"
      53             : #include "ogr_spatialref.h"
      54             : #include "ogrsf_frmts.h"
      55             : #include "ogr_osm.h"
      56             : #include "osm_parser.h"
      57             : #include "sqlite3.h"
      58             : 
      59             : constexpr int SWITCH_THRESHOLD = 10000;
      60             : constexpr int MAX_THRESHOLD = 100000;
      61             : 
      62             : /************************************************************************/
      63             : /*                          OGROSMLayer()                               */
      64             : /************************************************************************/
      65             : 
      66         155 : OGROSMLayer::OGROSMLayer(OGROSMDataSource *poDSIn, int nIdxLayerIn,
      67         155 :                          const char *pszName)
      68             :     : m_poDS(poDSIn), m_nIdxLayer(nIdxLayerIn),
      69         155 :       m_poFeatureDefn(new OGRFeatureDefn(pszName)),
      70         310 :       m_poSRS(new OGRSpatialReference())
      71             : {
      72         155 :     SetDescription(m_poFeatureDefn->GetName());
      73         155 :     m_poFeatureDefn->Reference();
      74             : 
      75         155 :     m_poSRS->SetWellKnownGeogCS("WGS84");
      76         155 :     m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
      77             : 
      78         155 :     if (m_poFeatureDefn->GetGeomFieldCount() != 0)
      79         155 :         m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poSRS);
      80         155 : }
      81             : 
      82             : /************************************************************************/
      83             : /*                          ~OGROSMLayer()                           */
      84             : /************************************************************************/
      85             : 
      86         310 : OGROSMLayer::~OGROSMLayer()
      87             : {
      88         155 :     m_poFeatureDefn->Release();
      89             : 
      90         155 :     if (m_poSRS)
      91         155 :         m_poSRS->Release();
      92             : 
      93         513 :     for (int i = 0; i < m_nFeatureArraySize; i++)
      94             :     {
      95         358 :         if (m_papoFeatures[i])
      96         356 :             delete m_papoFeatures[i];
      97             :     }
      98             : 
      99        1721 :     for (int i = 0; i < static_cast<int>(m_apszNames.size()); i++)
     100        1566 :         CPLFree(m_apszNames[i]);
     101             : 
     102         334 :     for (int i = 0; i < static_cast<int>(apszInsignificantKeys.size()); i++)
     103         179 :         CPLFree(apszInsignificantKeys[i]);
     104             : 
     105        1740 :     for (int i = 0; i < static_cast<int>(apszIgnoreKeys.size()); i++)
     106        1585 :         CPLFree(apszIgnoreKeys[i]);
     107             : 
     108         185 :     for (int i = 0; i < static_cast<int>(m_oComputedAttributes.size()); i++)
     109             :     {
     110          30 :         sqlite3_finalize(m_oComputedAttributes[i].hStmt);
     111             :     }
     112             : 
     113         155 :     CPLFree(m_papoFeatures);
     114         310 : }
     115             : 
     116             : /************************************************************************/
     117             : /*                            ResetReading()                            */
     118             : /************************************************************************/
     119             : 
     120          63 : void OGROSMLayer::ResetReading()
     121             : {
     122          63 :     if (!m_bResetReadingAllowed || m_poDS->IsInterleavedReading())
     123          48 :         return;
     124             : 
     125          15 :     m_poDS->MyResetReading();
     126             : }
     127             : 
     128             : /************************************************************************/
     129             : /*                        ForceResetReading()                           */
     130             : /************************************************************************/
     131             : 
     132         310 : void OGROSMLayer::ForceResetReading()
     133             : {
     134         321 :     for (int i = 0; i < m_nFeatureArraySize; i++)
     135             :     {
     136          11 :         if (m_papoFeatures[i])
     137           9 :             delete m_papoFeatures[i];
     138             :     }
     139         310 :     m_nFeatureArrayIndex = 0;
     140         310 :     m_nFeatureArraySize = 0;
     141         310 :     m_nFeatureCount = 0;
     142         310 :     m_bResetReadingAllowed = false;
     143         310 : }
     144             : 
     145             : /************************************************************************/
     146             : /*                        SetAttributeFilter()                          */
     147             : /************************************************************************/
     148             : 
     149          39 : OGRErr OGROSMLayer::SetAttributeFilter(const char *pszAttrQuery)
     150             : {
     151          39 :     if (pszAttrQuery == nullptr && m_pszAttrQueryString == nullptr)
     152          11 :         return OGRERR_NONE;
     153          28 :     if (pszAttrQuery != nullptr && m_pszAttrQueryString != nullptr &&
     154          14 :         strcmp(pszAttrQuery, m_pszAttrQueryString) == 0)
     155           0 :         return OGRERR_NONE;
     156             : 
     157          28 :     OGRErr eErr = OGRLayer::SetAttributeFilter(pszAttrQuery);
     158          28 :     if (eErr != OGRERR_NONE)
     159           0 :         return eErr;
     160             : 
     161          28 :     if (m_nFeatureArrayIndex == 0)
     162             :     {
     163          28 :         if (!m_poDS->IsInterleavedReading())
     164             :         {
     165          28 :             m_poDS->MyResetReading();
     166             :         }
     167             :     }
     168             :     else
     169             :     {
     170           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     171             :                  "The new attribute filter will "
     172             :                  "not be taken into account immediately. It is advised to "
     173             :                  "set attribute filters for all needed layers, before "
     174             :                  "reading *any* layer");
     175             :     }
     176             : 
     177          28 :     return OGRERR_NONE;
     178             : }
     179             : 
     180             : /************************************************************************/
     181             : /*                          GetFeatureCount()                           */
     182             : /************************************************************************/
     183             : 
     184           0 : GIntBig OGROSMLayer::GetFeatureCount(int bForce)
     185             : {
     186           0 :     if (m_poDS->IsFeatureCountEnabled())
     187           0 :         return OGRLayer::GetFeatureCount(bForce);
     188             : 
     189           0 :     return -1;
     190             : }
     191             : 
     192             : /************************************************************************/
     193             : /*                           GetNextFeature()                           */
     194             : /************************************************************************/
     195             : 
     196         237 : OGRFeature *OGROSMLayer::GetNextFeature()
     197             : {
     198         237 :     OGROSMLayer *poNewCurLayer = nullptr;
     199         237 :     OGRFeature *poFeature = MyGetNextFeature(&poNewCurLayer, nullptr, nullptr);
     200         237 :     m_poDS->SetCurrentLayer(poNewCurLayer);
     201         237 :     return poFeature;
     202             : }
     203             : 
     204         312 : OGRFeature *OGROSMLayer::MyGetNextFeature(OGROSMLayer **ppoNewCurLayer,
     205             :                                           GDALProgressFunc pfnProgress,
     206             :                                           void *pProgressData)
     207             : {
     208         312 :     *ppoNewCurLayer = m_poDS->GetCurrentLayer();
     209         312 :     m_bResetReadingAllowed = true;
     210             : 
     211         312 :     if (m_nFeatureArraySize == 0)
     212             :     {
     213         104 :         if (m_poDS->IsInterleavedReading())
     214             :         {
     215          36 :             if (*ppoNewCurLayer == nullptr)
     216             :             {
     217           0 :                 *ppoNewCurLayer = this;
     218             :             }
     219          36 :             else if (*ppoNewCurLayer != this)
     220             :             {
     221           0 :                 return nullptr;
     222             :             }
     223             : 
     224             :             // If too many features have been accumulated in another layer, we
     225             :             // force a switch to that layer, so that it gets emptied.
     226         216 :             for (int i = 0; i < m_poDS->GetLayerCount(); i++)
     227             :             {
     228         180 :                 if (m_poDS->m_papoLayers[i] != this &&
     229         144 :                     m_poDS->m_papoLayers[i]->m_nFeatureArraySize >
     230             :                         SWITCH_THRESHOLD)
     231             :                 {
     232           0 :                     *ppoNewCurLayer = m_poDS->m_papoLayers[i];
     233           0 :                     CPLDebug("OSM",
     234             :                              "Switching to '%s' as they are too many "
     235             :                              "features in '%s'",
     236           0 :                              m_poDS->m_papoLayers[i]->GetName(), GetName());
     237           0 :                     return nullptr;
     238             :                 }
     239             :             }
     240             : 
     241             :             // Read some more data and accumulate features.
     242          36 :             m_poDS->ParseNextChunk(m_nIdxLayer, pfnProgress, pProgressData);
     243             : 
     244          36 :             if (m_nFeatureArraySize == 0)
     245             :             {
     246             :                 // If there are really no more features to read in the
     247             :                 // current layer, force a switch to another non-empty layer.
     248             : 
     249         117 :                 for (int i = 0; i < m_poDS->GetLayerCount(); i++)
     250             :                 {
     251         109 :                     if (m_poDS->m_papoLayers[i] != this &&
     252          80 :                         m_poDS->m_papoLayers[i]->m_nFeatureArraySize > 0)
     253             :                     {
     254          21 :                         *ppoNewCurLayer = m_poDS->m_papoLayers[i];
     255          21 :                         CPLDebug("OSM",
     256             :                                  "Switching to '%s' as they are "
     257             :                                  "no more feature in '%s'",
     258          21 :                                  m_poDS->m_papoLayers[i]->GetName(), GetName());
     259          21 :                         return nullptr;
     260             :                     }
     261             :                 }
     262             : 
     263             :                 /* Game over : no more data to read from the stream */
     264           8 :                 *ppoNewCurLayer = nullptr;
     265           8 :                 return nullptr;
     266             :             }
     267             :         }
     268             :         else
     269             :         {
     270             :             while (true)
     271             :             {
     272             :                 int bRet =
     273          72 :                     m_poDS->ParseNextChunk(m_nIdxLayer, nullptr, nullptr);
     274             :                 // cppcheck-suppress knownConditionTrueFalse
     275          72 :                 if (m_nFeatureArraySize != 0)
     276          25 :                     break;
     277          47 :                 if (bRet == FALSE)
     278          43 :                     return nullptr;
     279           4 :             }
     280             :         }
     281             :     }
     282             : 
     283         240 :     OGRFeature *poFeature = m_papoFeatures[m_nFeatureArrayIndex];
     284             : 
     285         240 :     m_papoFeatures[m_nFeatureArrayIndex] = nullptr;
     286         240 :     m_nFeatureArrayIndex++;
     287             : 
     288         240 :     if (m_nFeatureArrayIndex == m_nFeatureArraySize)
     289          80 :         m_nFeatureArrayIndex = m_nFeatureArraySize = 0;
     290             : 
     291         240 :     return poFeature;
     292             : }
     293             : 
     294             : /************************************************************************/
     295             : /*                           TestCapability()                           */
     296             : /************************************************************************/
     297             : 
     298          36 : int OGROSMLayer::TestCapability(const char *pszCap)
     299             : {
     300          36 :     if (EQUAL(pszCap, OLCFastGetExtent))
     301             :     {
     302           0 :         OGREnvelope sExtent;
     303           0 :         if (m_poDS->GetExtent(&sExtent) == OGRERR_NONE)
     304           0 :             return TRUE;
     305             :     }
     306             : 
     307          36 :     return FALSE;
     308             : }
     309             : 
     310             : /************************************************************************/
     311             : /*                             AddToArray()                             */
     312             : /************************************************************************/
     313             : 
     314         605 : bool OGROSMLayer::AddToArray(OGRFeature *poFeature, int bCheckFeatureThreshold)
     315             : {
     316         605 :     if (bCheckFeatureThreshold && m_nFeatureArraySize > MAX_THRESHOLD)
     317             :     {
     318           0 :         if (!m_bHasWarnedTooManyFeatures)
     319             :         {
     320           0 :             CPLError(
     321             :                 CE_Failure, CPLE_AppDefined,
     322             :                 "Too many features have accumulated in %s layer. "
     323             :                 "Use the OGR_INTERLEAVED_READING=YES configuration option, "
     324             :                 "or the INTERLEAVED_READING=YES open option, or the "
     325             :                 "GDALDataset::GetNextFeature() / GDALDatasetGetNextFeature() "
     326             :                 "API.",
     327           0 :                 GetName());
     328             :         }
     329           0 :         m_bHasWarnedTooManyFeatures = true;
     330           0 :         return false;
     331             :     }
     332             : 
     333         605 :     if (m_nFeatureArraySize == m_nFeatureArrayMaxSize)
     334             :     {
     335          88 :         m_nFeatureArrayMaxSize =
     336          88 :             m_nFeatureArrayMaxSize + m_nFeatureArrayMaxSize / 2 + 128;
     337          88 :         CPLDebug("OSM", "For layer %s, new max size is %d", GetName(),
     338             :                  m_nFeatureArrayMaxSize);
     339             :         OGRFeature **papoNewFeatures =
     340          88 :             static_cast<OGRFeature **>(VSI_REALLOC_VERBOSE(
     341             :                 m_papoFeatures, m_nFeatureArrayMaxSize * sizeof(OGRFeature *)));
     342          88 :         if (papoNewFeatures == nullptr)
     343             :         {
     344           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     345             :                      "For layer %s, cannot resize feature array to %d features",
     346           0 :                      GetName(), m_nFeatureArrayMaxSize);
     347           0 :             return false;
     348             :         }
     349          88 :         m_papoFeatures = papoNewFeatures;
     350             :     }
     351         605 :     m_papoFeatures[m_nFeatureArraySize++] = poFeature;
     352             : 
     353         605 :     return true;
     354             : }
     355             : 
     356             : /************************************************************************/
     357             : /*                        EvaluateAttributeFilter()                     */
     358             : /************************************************************************/
     359             : 
     360          11 : int OGROSMLayer::EvaluateAttributeFilter(OGRFeature *poFeature)
     361             : {
     362          11 :     return (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature));
     363             : }
     364             : 
     365             : /************************************************************************/
     366             : /*                             AddFeature()                             */
     367             : /************************************************************************/
     368             : 
     369         640 : int OGROSMLayer::AddFeature(OGRFeature *poFeature,
     370             :                             int bAttrFilterAlreadyEvaluated, int *pbFilteredOut,
     371             :                             int bCheckFeatureThreshold)
     372             : {
     373         640 :     if (!m_bUserInterested)
     374             :     {
     375           0 :         if (pbFilteredOut)
     376           0 :             *pbFilteredOut = TRUE;
     377           0 :         delete poFeature;
     378           0 :         return TRUE;
     379             :     }
     380             : 
     381         640 :     OGRGeometry *poGeom = poFeature->GetGeometryRef();
     382         640 :     if (poGeom)
     383         640 :         poGeom->assignSpatialReference(m_poSRS);
     384             : 
     385        1280 :     if ((m_poFilterGeom == nullptr ||
     386        1280 :          FilterGeometry(poFeature->GetGeometryRef())) &&
     387         640 :         (m_poAttrQuery == nullptr || bAttrFilterAlreadyEvaluated ||
     388          40 :          m_poAttrQuery->Evaluate(poFeature)))
     389             :     {
     390         605 :         if (!AddToArray(poFeature, bCheckFeatureThreshold))
     391             :         {
     392           0 :             delete poFeature;
     393           0 :             return FALSE;
     394             :         }
     395             :     }
     396             :     else
     397             :     {
     398          35 :         if (pbFilteredOut)
     399          35 :             *pbFilteredOut = TRUE;
     400          35 :         delete poFeature;
     401          35 :         return TRUE;
     402             :     }
     403             : 
     404         605 :     if (pbFilteredOut)
     405         605 :         *pbFilteredOut = FALSE;
     406         605 :     return TRUE;
     407             : }
     408             : 
     409             : /************************************************************************/
     410             : /*                             GetExtent()                              */
     411             : /************************************************************************/
     412             : 
     413           3 : OGRErr OGROSMLayer::GetExtent(OGREnvelope *psExtent, int /* bForce */)
     414             : {
     415           3 :     if (m_poDS->GetExtent(psExtent) == OGRERR_NONE)
     416           3 :         return OGRERR_NONE;
     417             : 
     418             :     /* return OGRLayer::GetExtent(psExtent, bForce);*/
     419           0 :     return OGRERR_FAILURE;
     420             : }
     421             : 
     422             : /************************************************************************/
     423             : /*                          GetLaunderedFieldName()                     */
     424             : /************************************************************************/
     425             : 
     426        1566 : const char *OGROSMLayer::GetLaunderedFieldName(const char *pszName)
     427             : {
     428        3132 :     if (m_poDS->DoesAttributeNameLaundering() &&
     429        1566 :         strchr(pszName, ':') != nullptr)
     430             :     {
     431           2 :         size_t i = 0;
     432          17 :         for (; i < sizeof(szLaunderedFieldName) - 1 && pszName[i] != '\0'; i++)
     433             :         {
     434          15 :             if (pszName[i] == ':')
     435           2 :                 szLaunderedFieldName[i] = '_';
     436             :             else
     437          13 :                 szLaunderedFieldName[i] = pszName[i];
     438             :         }
     439           2 :         szLaunderedFieldName[i] = '\0';
     440           2 :         return szLaunderedFieldName;
     441             :     }
     442             : 
     443        1564 :     return pszName;
     444             : }
     445             : 
     446             : /************************************************************************/
     447             : /*                              AddField()                              */
     448             : /************************************************************************/
     449             : 
     450        1566 : void OGROSMLayer::AddField(const char *pszName, OGRFieldType eFieldType,
     451             :                            OGRFieldSubType eSubType)
     452             : {
     453        1566 :     const char *pszLaunderedName = GetLaunderedFieldName(pszName);
     454        3132 :     OGRFieldDefn oField(pszLaunderedName, eFieldType);
     455        1566 :     oField.SetSubType(eSubType);
     456        1566 :     m_poFeatureDefn->AddFieldDefn(&oField);
     457             : 
     458        1566 :     int nIndex = m_poFeatureDefn->GetFieldCount() - 1;
     459        1566 :     char *pszDupName = CPLStrdup(pszName);
     460        1566 :     m_apszNames.push_back(pszDupName);
     461        1566 :     m_oMapFieldNameToIndex[pszDupName] = nIndex;
     462             : 
     463        1566 :     if (strcmp(pszName, "osm_id") == 0)
     464         150 :         m_nIndexOSMId = nIndex;
     465             : 
     466        1416 :     else if (strcmp(pszName, "osm_way_id") == 0)
     467          30 :         m_nIndexOSMWayId = nIndex;
     468             : 
     469        1386 :     else if (strcmp(pszName, "other_tags") == 0)
     470         153 :         m_nIndexOtherTags = nIndex;
     471             : 
     472        1233 :     else if (strcmp(pszName, "all_tags") == 0)
     473           2 :         m_nIndexAllTags = nIndex;
     474        1566 : }
     475             : 
     476             : /************************************************************************/
     477             : /*                              GetFieldIndex()                         */
     478             : /************************************************************************/
     479             : 
     480        1092 : int OGROSMLayer::GetFieldIndex(const char *pszName)
     481             : {
     482             :     std::map<const char *, int, ConstCharComp>::iterator oIter =
     483        1092 :         m_oMapFieldNameToIndex.find(pszName);
     484        1092 :     if (oIter != m_oMapFieldNameToIndex.end())
     485         711 :         return oIter->second;
     486             : 
     487         381 :     return -1;
     488             : }
     489             : 
     490             : /************************************************************************/
     491             : /*                         AddInOtherOrAllTags()                        */
     492             : /************************************************************************/
     493             : 
     494         386 : int OGROSMLayer::AddInOtherOrAllTags(const char *pszK)
     495             : {
     496         386 :     bool bAddToOtherTags = false;
     497             : 
     498         386 :     if (aoSetIgnoreKeys.find(pszK) == aoSetIgnoreKeys.end())
     499             :     {
     500         186 :         char *pszColon = strchr((char *)pszK, ':');
     501         186 :         if (pszColon)
     502             :         {
     503           0 :             char chBackup = pszColon[1];
     504           0 :             pszColon[1] = '\0'; /* Evil but OK */
     505             :             bAddToOtherTags =
     506           0 :                 (aoSetIgnoreKeys.find(pszK) == aoSetIgnoreKeys.end());
     507             :             // cppcheck-suppress redundantAssignment
     508           0 :             pszColon[1] = chBackup;
     509             :         }
     510             :         else
     511         186 :             bAddToOtherTags = true;
     512             :     }
     513             : 
     514         386 :     return bAddToOtherTags;
     515             : }
     516             : 
     517             : /************************************************************************/
     518             : /*                     OGROSMEscapeStringHSTORE()                       */
     519             : /************************************************************************/
     520             : 
     521         364 : static void OGROSMEscapeStringHSTORE(const char *pszV, std::string &sOut)
     522             : {
     523         364 :     sOut += '"';
     524             : 
     525        2556 :     for (int k = 0; pszV[k] != '\0'; k++)
     526             :     {
     527        2192 :         if (pszV[k] == '"' || pszV[k] == '\\')
     528           0 :             sOut += '\\';
     529        2192 :         sOut += pszV[k];
     530             :     }
     531             : 
     532         364 :     sOut += '"';
     533         364 : }
     534             : 
     535             : /************************************************************************/
     536             : /*                     OGROSMEscapeStringJSON()                         */
     537             : /************************************************************************/
     538             : 
     539           8 : static void OGROSMEscapeStringJSON(const char *pszV, std::string &sOut)
     540             : {
     541           8 :     sOut += '"';
     542             : 
     543          37 :     for (int k = 0; pszV[k] != '\0'; k++)
     544             :     {
     545          29 :         const char ch = pszV[k];
     546          29 :         switch (ch)
     547             :         {
     548           1 :             case '"':
     549           1 :                 sOut += "\\\"";
     550           1 :                 break;
     551           1 :             case '\\':
     552           1 :                 sOut += "\\\\";
     553           1 :                 break;
     554           1 :             case '\n':
     555           1 :                 sOut += "\\n";
     556           1 :                 break;
     557           1 :             case '\r':
     558           1 :                 sOut += "\\r";
     559           1 :                 break;
     560           1 :             case '\t':
     561           1 :                 sOut += "\\t";
     562           1 :                 break;
     563          24 :             default:
     564          24 :                 if (static_cast<unsigned char>(ch) < ' ')
     565           0 :                     sOut += CPLSPrintf("\\u%04X", ch);
     566             :                 else
     567          24 :                     sOut += ch;
     568          24 :                 break;
     569             :         }
     570             :     }
     571             : 
     572           8 :     sOut += '"';
     573           8 : }
     574             : 
     575             : /************************************************************************/
     576             : /*                            GetValueOfTag()                           */
     577             : /************************************************************************/
     578             : 
     579        1176 : static const char *GetValueOfTag(const char *pszKeyToSearch, unsigned int nTags,
     580             :                                  const OSMTag *pasTags)
     581             : {
     582        3088 :     for (unsigned int k = 0; k < nTags; k++)
     583             :     {
     584        1916 :         const char *pszK = pasTags[k].pszK;
     585        1916 :         if (strcmp(pszK, pszKeyToSearch) == 0)
     586             :         {
     587           4 :             return pasTags[k].pszV;
     588             :         }
     589             :     }
     590        1172 :     return nullptr;
     591             : }
     592             : 
     593             : /************************************************************************/
     594             : /*                        SetFieldsFromTags()                           */
     595             : /************************************************************************/
     596             : 
     597         668 : void OGROSMLayer::SetFieldsFromTags(OGRFeature *poFeature, GIntBig nID,
     598             :                                     bool bIsWayID, unsigned int nTags,
     599             :                                     OSMTag *pasTags, OSMInfo *psInfo)
     600             : {
     601         668 :     if (!bIsWayID)
     602             :     {
     603         527 :         poFeature->SetFID(nID);
     604             : 
     605         527 :         if (m_bHasOSMId)
     606             :         {
     607             :             char szID[32];
     608         526 :             snprintf(szID, sizeof(szID), CPL_FRMT_GIB, nID);
     609         526 :             poFeature->SetField(m_nIndexOSMId, szID);
     610             :         }
     611             :     }
     612             :     else
     613             :     {
     614         141 :         poFeature->SetFID(nID);
     615             : 
     616         141 :         if (m_nIndexOSMWayId >= 0)
     617             :         {
     618             :             char szID[32];
     619         141 :             snprintf(szID, sizeof(szID), CPL_FRMT_GIB, nID);
     620         141 :             poFeature->SetField(m_nIndexOSMWayId, szID);
     621             :         }
     622             :     }
     623             : 
     624         668 :     if (m_bHasVersion)
     625             :     {
     626           0 :         poFeature->SetField("osm_version", psInfo->nVersion);
     627             :     }
     628         668 :     if (m_bHasTimestamp)
     629             :     {
     630           0 :         if (psInfo->bTimeStampIsStr)
     631             :         {
     632             :             OGRField sField;
     633           0 :             if (OGRParseXMLDateTime(psInfo->ts.pszTimeStamp, &sField))
     634             :             {
     635           0 :                 poFeature->SetField("osm_timestamp", &sField);
     636             :             }
     637             :         }
     638             :         else
     639             :         {
     640             :             struct tm brokendown;
     641           0 :             CPLUnixTimeToYMDHMS(psInfo->ts.nTimeStamp, &brokendown);
     642           0 :             poFeature->SetField("osm_timestamp", brokendown.tm_year + 1900,
     643           0 :                                 brokendown.tm_mon + 1, brokendown.tm_mday,
     644             :                                 brokendown.tm_hour, brokendown.tm_min,
     645           0 :                                 static_cast<float>(brokendown.tm_sec), 0);
     646             :         }
     647             :     }
     648         668 :     if (m_bHasUID)
     649             :     {
     650           0 :         poFeature->SetField("osm_uid", psInfo->nUID);
     651             :     }
     652         668 :     if (m_bHasUser)
     653             :     {
     654           0 :         poFeature->SetField("osm_user", psInfo->pszUserSID);
     655             :     }
     656         668 :     if (m_bHasChangeset)
     657             :     {
     658           0 :         poFeature->SetField("osm_changeset", (int)psInfo->nChangeset);
     659             :     }
     660             : 
     661         668 :     m_osAllTagsBuffer.clear();
     662        1760 :     for (unsigned int j = 0; j < nTags; j++)
     663             :     {
     664        1092 :         const char *pszK = pasTags[j].pszK;
     665        1092 :         const char *pszV = pasTags[j].pszV;
     666        1092 :         int nIndex = GetFieldIndex(pszK);
     667        1092 :         if (nIndex >= 0 && nIndex != m_nIndexOSMId)
     668             :         {
     669         710 :             poFeature->SetField(nIndex, pszV);
     670         710 :             if (m_nIndexAllTags < 0)
     671         706 :                 continue;
     672             :         }
     673         386 :         if (m_nIndexAllTags >= 0 || m_nIndexOtherTags >= 0)
     674             :         {
     675         386 :             if (AddInOtherOrAllTags(pszK))
     676             :             {
     677         186 :                 if (m_poDS->m_bTagsAsHSTORE)
     678             :                 {
     679         182 :                     if (!m_osAllTagsBuffer.empty())
     680          58 :                         m_osAllTagsBuffer += ',';
     681             : 
     682         182 :                     OGROSMEscapeStringHSTORE(pszK, m_osAllTagsBuffer);
     683             : 
     684         182 :                     m_osAllTagsBuffer += '=';
     685         182 :                     m_osAllTagsBuffer += '>';
     686             : 
     687         182 :                     OGROSMEscapeStringHSTORE(pszV, m_osAllTagsBuffer);
     688             :                 }
     689             :                 else
     690             :                 {
     691           4 :                     if (!m_osAllTagsBuffer.empty())
     692           1 :                         m_osAllTagsBuffer += ',';
     693             :                     else
     694           3 :                         m_osAllTagsBuffer = '{';
     695           4 :                     OGROSMEscapeStringJSON(pszK, m_osAllTagsBuffer);
     696           4 :                     m_osAllTagsBuffer += ':';
     697           4 :                     OGROSMEscapeStringJSON(pszV, m_osAllTagsBuffer);
     698             :                 }
     699             :             }
     700             : 
     701             : #ifdef notdef
     702             :             if (aoSetWarnKeys.find(pszK) == aoSetWarnKeys.end())
     703             :             {
     704             :                 aoSetWarnKeys.insert(pszK);
     705             :                 CPLDebug("OSM_KEY", "Ignored key : %s", pszK);
     706             :             }
     707             : #endif
     708             :         }
     709             :     }
     710             : 
     711         668 :     if (!m_osAllTagsBuffer.empty())
     712             :     {
     713         127 :         if (!m_poDS->m_bTagsAsHSTORE)
     714             :         {
     715           3 :             m_osAllTagsBuffer += '}';
     716             :         }
     717         127 :         if (m_nIndexAllTags >= 0)
     718           4 :             poFeature->SetField(m_nIndexAllTags, m_osAllTagsBuffer.c_str());
     719             :         else
     720         123 :             poFeature->SetField(m_nIndexOtherTags, m_osAllTagsBuffer.c_str());
     721             :     }
     722             : 
     723        1063 :     for (size_t i = 0; i < m_oComputedAttributes.size(); i++)
     724             :     {
     725         395 :         const OGROSMComputedAttribute &oAttr = m_oComputedAttributes[i];
     726         395 :         if (oAttr.bHardcodedZOrder)
     727             :         {
     728         392 :             const int nHighwayIdx = oAttr.anIndexToBind[0];
     729         392 :             const int nBridgeIdx = oAttr.anIndexToBind[1];
     730         392 :             const int nTunnelIdx = oAttr.anIndexToBind[2];
     731         392 :             const int nRailwayIdx = oAttr.anIndexToBind[3];
     732         392 :             const int nLayerIdx = oAttr.anIndexToBind[4];
     733             : 
     734         392 :             int nZOrder = 0;
     735             :             /*
     736             :                 "SELECT (CASE [highway] WHEN 'minor' THEN 3 WHEN 'road' THEN 3 "
     737             :                 "WHEN 'unclassified' THEN 3 WHEN 'residential' THEN 3 WHEN "
     738             :                 "'tertiary_link' THEN 4 WHEN 'tertiary' THEN 4 WHEN
     739             :                'secondary_link' " "THEN 6 WHEN 'secondary' THEN 6 WHEN
     740             :                'primary_link' THEN 7 WHEN "
     741             :                 "'primary' THEN 7 WHEN 'trunk_link' THEN 8 WHEN 'trunk' THEN 8 "
     742             :                 "WHEN 'motorway_link' THEN 9 WHEN 'motorway' THEN 9 ELSE 0 END)
     743             :                + "
     744             :                 "(CASE WHEN [bridge] IN ('yes', 'true', '1') THEN 10 ELSE 0 END)
     745             :                + "
     746             :                 "(CASE WHEN [tunnel] IN ('yes', 'true', '1') THEN -10 ELSE 0
     747             :                END) + "
     748             :                 "(CASE WHEN [railway] IS NOT NULL THEN 5 ELSE 0 END) + "
     749             :                 "(CASE WHEN [layer] IS NOT NULL THEN 10 * CAST([layer] AS
     750             :                INTEGER) " */
     751             : 
     752         392 :             const char *pszHighway = nullptr;
     753         392 :             if (nHighwayIdx >= 0)
     754             :             {
     755         392 :                 if (poFeature->IsFieldSetAndNotNull(nHighwayIdx))
     756             :                 {
     757         296 :                     pszHighway = poFeature->GetFieldAsString(nHighwayIdx);
     758             :                 }
     759             :             }
     760             :             else
     761           0 :                 pszHighway = GetValueOfTag("highway", nTags, pasTags);
     762         392 :             if (pszHighway)
     763             :             {
     764         296 :                 if (strcmp(pszHighway, "minor") == 0 ||
     765         296 :                     strcmp(pszHighway, "road") == 0 ||
     766         288 :                     strcmp(pszHighway, "unclassified") == 0 ||
     767         236 :                     strcmp(pszHighway, "residential") == 0)
     768             :                 {
     769         228 :                     nZOrder += 3;
     770             :                 }
     771          68 :                 else if (strcmp(pszHighway, "tertiary_link") == 0 ||
     772          68 :                          strcmp(pszHighway, "tertiary") == 0)
     773             :                 {
     774          18 :                     nZOrder += 4;
     775             :                 }
     776          50 :                 else if (strcmp(pszHighway, "secondary_link") == 0 ||
     777          50 :                          strcmp(pszHighway, "secondary") == 0)
     778             :                 {
     779           2 :                     nZOrder += 6;
     780             :                 }
     781          48 :                 else if (strcmp(pszHighway, "primary_link") == 0 ||
     782          48 :                          strcmp(pszHighway, "primary") == 0)
     783             :                 {
     784           4 :                     nZOrder += 7;
     785             :                 }
     786          44 :                 else if (strcmp(pszHighway, "trunk_link") == 0 ||
     787          44 :                          strcmp(pszHighway, "trunk") == 0)
     788             :                 {
     789           0 :                     nZOrder += 8;
     790             :                 }
     791          44 :                 else if (strcmp(pszHighway, "motorway_link") == 0 ||
     792          44 :                          strcmp(pszHighway, "motorway") == 0)
     793             :                 {
     794          20 :                     nZOrder += 9;
     795             :                 }
     796             :             }
     797             : 
     798         392 :             const char *pszBridge = nullptr;
     799         392 :             if (nBridgeIdx >= 0)
     800             :             {
     801           0 :                 if (poFeature->IsFieldSetAndNotNull(nBridgeIdx))
     802             :                 {
     803           0 :                     pszBridge = poFeature->GetFieldAsString(nBridgeIdx);
     804             :                 }
     805             :             }
     806             :             else
     807         392 :                 pszBridge = GetValueOfTag("bridge", nTags, pasTags);
     808         392 :             if (pszBridge)
     809             :             {
     810           0 :                 if (strcmp(pszBridge, "yes") == 0 ||
     811           0 :                     strcmp(pszBridge, "true") == 0 ||
     812           0 :                     strcmp(pszBridge, "1") == 0)
     813             :                 {
     814           0 :                     nZOrder += 10;
     815             :                 }
     816             :             }
     817             : 
     818         392 :             const char *pszTunnel = nullptr;
     819         392 :             if (nTunnelIdx >= 0)
     820             :             {
     821           0 :                 if (poFeature->IsFieldSetAndNotNull(nTunnelIdx))
     822             :                 {
     823           0 :                     pszTunnel = poFeature->GetFieldAsString(nTunnelIdx);
     824             :                 }
     825             :             }
     826             :             else
     827         392 :                 pszTunnel = GetValueOfTag("tunnel", nTags, pasTags);
     828         392 :             if (pszTunnel)
     829             :             {
     830           0 :                 if (strcmp(pszTunnel, "yes") == 0 ||
     831           0 :                     strcmp(pszTunnel, "true") == 0 ||
     832           0 :                     strcmp(pszTunnel, "1") == 0)
     833             :                 {
     834           0 :                     nZOrder -= 10;
     835             :                 }
     836             :             }
     837             : 
     838         392 :             const char *pszRailway = nullptr;
     839         392 :             if (nRailwayIdx >= 0)
     840             :             {
     841         392 :                 if (poFeature->IsFieldSetAndNotNull(nRailwayIdx))
     842             :                 {
     843           4 :                     pszRailway = poFeature->GetFieldAsString(nRailwayIdx);
     844             :                 }
     845             :             }
     846             :             else
     847           0 :                 pszRailway = GetValueOfTag("railway", nTags, pasTags);
     848         392 :             if (pszRailway)
     849             :             {
     850           4 :                 nZOrder += 5;
     851             :             }
     852             : 
     853         392 :             const char *pszLayer = nullptr;
     854         392 :             if (nLayerIdx >= 0)
     855             :             {
     856           0 :                 if (poFeature->IsFieldSetAndNotNull(nLayerIdx))
     857             :                 {
     858           0 :                     pszLayer = poFeature->GetFieldAsString(nLayerIdx);
     859             :                 }
     860             :             }
     861             :             else
     862         392 :                 pszLayer = GetValueOfTag("layer", nTags, pasTags);
     863         392 :             if (pszLayer)
     864             :             {
     865           4 :                 nZOrder += 10 * atoi(pszLayer);
     866             :             }
     867             : 
     868         392 :             poFeature->SetField(oAttr.nIndex, nZOrder);
     869             : 
     870         392 :             continue;
     871             :         }
     872             : 
     873          21 :         for (int j = 0; j < static_cast<int>(oAttr.anIndexToBind.size()); j++)
     874             :         {
     875          18 :             if (oAttr.anIndexToBind[j] >= 0)
     876             :             {
     877           3 :                 if (!poFeature->IsFieldSetAndNotNull(oAttr.anIndexToBind[j]))
     878             :                 {
     879           2 :                     sqlite3_bind_null(oAttr.hStmt, j + 1);
     880             :                 }
     881             :                 else
     882             :                 {
     883             :                     OGRFieldType eType =
     884           1 :                         m_poFeatureDefn->GetFieldDefn(oAttr.anIndexToBind[j])
     885           1 :                             ->GetType();
     886           1 :                     if (eType == OFTInteger)
     887           0 :                         sqlite3_bind_int(oAttr.hStmt, j + 1,
     888             :                                          poFeature->GetFieldAsInteger(
     889           0 :                                              oAttr.anIndexToBind[j]));
     890           1 :                     else if (eType == OFTInteger64)
     891           0 :                         sqlite3_bind_int64(oAttr.hStmt, j + 1,
     892             :                                            poFeature->GetFieldAsInteger64(
     893           0 :                                                oAttr.anIndexToBind[j]));
     894           1 :                     else if (eType == OFTReal)
     895           0 :                         sqlite3_bind_double(oAttr.hStmt, j + 1,
     896             :                                             poFeature->GetFieldAsDouble(
     897           0 :                                                 oAttr.anIndexToBind[j]));
     898             :                     else
     899           1 :                         sqlite3_bind_text(
     900           1 :                             oAttr.hStmt, j + 1,
     901           1 :                             poFeature->GetFieldAsString(oAttr.anIndexToBind[j]),
     902             :                             -1, SQLITE_TRANSIENT);
     903             :                 }
     904             :             }
     905             :             else
     906             :             {
     907          15 :                 bool bTagFound = false;
     908          35 :                 for (unsigned int k = 0; k < nTags; k++)
     909             :                 {
     910          20 :                     const char *pszK = pasTags[k].pszK;
     911          20 :                     const char *pszV = pasTags[k].pszV;
     912          20 :                     if (strcmp(pszK, oAttr.aosAttrToBind[j]) == 0)
     913             :                     {
     914           0 :                         sqlite3_bind_text(oAttr.hStmt, j + 1, pszV, -1,
     915             :                                           SQLITE_TRANSIENT);
     916           0 :                         bTagFound = true;
     917           0 :                         break;
     918             :                     }
     919             :                 }
     920          15 :                 if (!bTagFound)
     921          15 :                     sqlite3_bind_null(oAttr.hStmt, j + 1);
     922             :             }
     923             :         }
     924             : 
     925           6 :         if (sqlite3_step(oAttr.hStmt) == SQLITE_ROW &&
     926           3 :             sqlite3_column_count(oAttr.hStmt) == 1)
     927             :         {
     928           3 :             switch (sqlite3_column_type(oAttr.hStmt, 0))
     929             :             {
     930           3 :                 case SQLITE_INTEGER:
     931           3 :                     poFeature->SetField(
     932           3 :                         oAttr.nIndex,
     933           3 :                         (GIntBig)sqlite3_column_int64(oAttr.hStmt, 0));
     934           3 :                     break;
     935           0 :                 case SQLITE_FLOAT:
     936           0 :                     poFeature->SetField(oAttr.nIndex,
     937           0 :                                         sqlite3_column_double(oAttr.hStmt, 0));
     938           0 :                     break;
     939           0 :                 case SQLITE_TEXT:
     940           0 :                     poFeature->SetField(
     941           0 :                         oAttr.nIndex,
     942           0 :                         (const char *)sqlite3_column_text(oAttr.hStmt, 0));
     943           0 :                     break;
     944           0 :                 default:
     945           0 :                     break;
     946             :             }
     947             :         }
     948             : 
     949           3 :         sqlite3_reset(oAttr.hStmt);
     950             :     }
     951         668 : }
     952             : 
     953             : /************************************************************************/
     954             : /*                      GetSpatialFilterEnvelope()                      */
     955             : /************************************************************************/
     956             : 
     957          75 : const OGREnvelope *OGROSMLayer::GetSpatialFilterEnvelope()
     958             : {
     959          75 :     if (m_poFilterGeom != nullptr)
     960           1 :         return &m_sFilterEnvelope;
     961             :     else
     962          74 :         return nullptr;
     963             : }
     964             : 
     965             : /************************************************************************/
     966             : /*                        AddInsignificantKey()                         */
     967             : /************************************************************************/
     968             : 
     969         179 : void OGROSMLayer::AddInsignificantKey(const char *pszK)
     970             : {
     971         179 :     char *pszKDup = CPLStrdup(pszK);
     972         179 :     apszInsignificantKeys.push_back(pszKDup);
     973         179 :     aoSetInsignificantKeys[pszKDup] = 1;
     974         179 : }
     975             : 
     976             : /************************************************************************/
     977             : /*                          AddIgnoreKey()                              */
     978             : /************************************************************************/
     979             : 
     980        1585 : void OGROSMLayer::AddIgnoreKey(const char *pszK)
     981             : {
     982        1585 :     char *pszKDup = CPLStrdup(pszK);
     983        1585 :     apszIgnoreKeys.push_back(pszKDup);
     984        1585 :     aoSetIgnoreKeys[pszKDup] = 1;
     985        1585 : }
     986             : 
     987             : /************************************************************************/
     988             : /*                           AddWarnKey()                               */
     989             : /************************************************************************/
     990             : 
     991        1585 : void OGROSMLayer::AddWarnKey(const char *pszK)
     992             : {
     993        1585 :     aoSetWarnKeys.insert(pszK);
     994        1585 : }
     995             : 
     996             : /************************************************************************/
     997             : /*                           AddWarnKey()                               */
     998             : /************************************************************************/
     999             : 
    1000          30 : void OGROSMLayer::AddComputedAttribute(const char *pszName, OGRFieldType eType,
    1001             :                                        const char *pszSQL)
    1002             : {
    1003          30 :     if (m_poDS->m_hDBForComputedAttributes == nullptr)
    1004             :     {
    1005          60 :         const int rc = sqlite3_open_v2(
    1006          30 :             ":memory:", &(m_poDS->m_hDBForComputedAttributes),
    1007             :             SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX,
    1008             :             nullptr);
    1009          30 :         if (rc != SQLITE_OK)
    1010             :         {
    1011           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1012             :                      "Cannot open temporary sqlite DB");
    1013           0 :             return;
    1014             :         }
    1015             :     }
    1016             : 
    1017          30 :     if (m_poFeatureDefn->GetFieldIndex(pszName) >= 0)
    1018             :     {
    1019           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1020             :                  "A field with same name %s already exists", pszName);
    1021           0 :         return;
    1022             :     }
    1023             : 
    1024          30 :     CPLString osSQL(pszSQL);
    1025          30 :     const bool bHardcodedZOrder =
    1026          60 :         (eType == OFTInteger) &&
    1027          30 :         strcmp(
    1028             :             pszSQL,
    1029             :             "SELECT (CASE [highway] WHEN 'minor' THEN 3 WHEN 'road' THEN 3 "
    1030             :             "WHEN 'unclassified' THEN 3 WHEN 'residential' THEN 3 WHEN "
    1031             :             "'tertiary_link' THEN 4 WHEN 'tertiary' THEN 4 WHEN "
    1032             :             "'secondary_link' "
    1033             :             "THEN 6 WHEN 'secondary' THEN 6 WHEN 'primary_link' THEN 7 WHEN "
    1034             :             "'primary' THEN 7 WHEN 'trunk_link' THEN 8 WHEN 'trunk' THEN 8 "
    1035             :             "WHEN 'motorway_link' THEN 9 WHEN 'motorway' THEN 9 ELSE 0 END) + "
    1036             :             "(CASE WHEN [bridge] IN ('yes', 'true', '1') THEN 10 ELSE 0 END) + "
    1037             :             "(CASE WHEN [tunnel] IN ('yes', 'true', '1') THEN -10 ELSE 0 END) "
    1038             :             "+ "
    1039             :             "(CASE WHEN [railway] IS NOT NULL THEN 5 ELSE 0 END) + "
    1040             :             "(CASE WHEN [layer] IS NOT NULL THEN 10 * CAST([layer] AS INTEGER) "
    1041             :             "ELSE 0 END)") == 0;
    1042          30 :     std::vector<CPLString> aosAttrToBind;
    1043          30 :     std::vector<int> anIndexToBind;
    1044          30 :     size_t nStartSearch = 0;
    1045             :     while (true)
    1046             :     {
    1047         210 :         size_t nPos = osSQL.find("[", nStartSearch);
    1048         210 :         if (nPos == std::string::npos)
    1049          30 :             break;
    1050         180 :         nStartSearch = nPos + 1;
    1051         180 :         if (nPos > 0 && osSQL[nPos - 1] != '\\')
    1052             :         {
    1053         180 :             CPLString osAttr = osSQL.substr(nPos + 1);
    1054         180 :             size_t nPos2 = osAttr.find("]");
    1055         180 :             if (nPos2 == std::string::npos)
    1056           0 :                 break;
    1057         180 :             osAttr.resize(nPos2);
    1058             : 
    1059         360 :             osSQL = osSQL.substr(0, nPos) + "?" +
    1060         540 :                     osSQL.substr(nPos + 1 + nPos2 + 1);
    1061             : 
    1062         180 :             aosAttrToBind.push_back(osAttr);
    1063         180 :             anIndexToBind.push_back(m_poFeatureDefn->GetFieldIndex(osAttr));
    1064             :         }
    1065         180 :     }
    1066             :     while (true)
    1067             :     {
    1068          30 :         size_t nPos = osSQL.find("\\");
    1069          30 :         if (nPos == std::string::npos || nPos == osSQL.size() - 1)
    1070          30 :             break;
    1071           0 :         osSQL = osSQL.substr(0, nPos) + osSQL.substr(nPos + 1);
    1072           0 :     }
    1073             : 
    1074          30 :     CPLDebug("OSM", "SQL : \"%s\"", osSQL.c_str());
    1075             : 
    1076          30 :     sqlite3_stmt *hStmt = nullptr;
    1077          30 :     int rc = sqlite3_prepare_v2(m_poDS->m_hDBForComputedAttributes, osSQL, -1,
    1078             :                                 &hStmt, nullptr);
    1079          30 :     if (rc != SQLITE_OK)
    1080             :     {
    1081           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1082             :                  "sqlite3_prepare_v2() failed :  %s",
    1083           0 :                  sqlite3_errmsg(m_poDS->m_hDBForComputedAttributes));
    1084           0 :         return;
    1085             :     }
    1086             : 
    1087          30 :     OGRFieldDefn oField(pszName, eType);
    1088          30 :     m_poFeatureDefn->AddFieldDefn(&oField);
    1089          30 :     m_oComputedAttributes.push_back(OGROSMComputedAttribute(pszName));
    1090          30 :     OGROSMComputedAttribute &oComputedAttribute = m_oComputedAttributes.back();
    1091          30 :     oComputedAttribute.eType = eType;
    1092          30 :     oComputedAttribute.nIndex = m_poFeatureDefn->GetFieldCount() - 1;
    1093          30 :     oComputedAttribute.osSQL = pszSQL;
    1094          30 :     oComputedAttribute.hStmt = hStmt;
    1095          30 :     oComputedAttribute.aosAttrToBind = std::move(aosAttrToBind);
    1096          30 :     oComputedAttribute.anIndexToBind = std::move(anIndexToBind);
    1097          30 :     oComputedAttribute.bHardcodedZOrder = bHardcodedZOrder;
    1098             : }

Generated by: LCOV version 1.14