LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/georss - ogrgeorsslayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1041 1190 87.5 %
Date: 2026-10-23 13:59:38 Functions: 31 32 96.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GeoRSS Translator
       4             :  * Purpose:  Implements OGRGeoRSSLayer class.
       5             :  * Author:   Even Rouault, even dot rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "ogr_georss.h"
      15             : 
      16             : #include <cstddef>
      17             : #include <cstdio>
      18             : #include <cstdlib>
      19             : #include <cstring>
      20             : 
      21             : #include "cpl_conv.h"
      22             : #include "cpl_error.h"
      23             : #include "cpl_hash_set.h"
      24             : #include "cpl_minixml.h"
      25             : #include "cpl_string.h"
      26             : #include "cpl_vsi.h"
      27             : #ifdef HAVE_EXPAT
      28             : #include "expat.h"
      29             : #endif
      30             : #include "ogr_api.h"
      31             : #include "ogr_core.h"
      32             : #include "ogr_expat.h"
      33             : #include "ogr_feature.h"
      34             : #include "ogr_geometry.h"
      35             : #include "ogr_p.h"
      36             : #include "ogr_spatialref.h"
      37             : #include "ogrsf_frmts.h"
      38             : 
      39             : static const char *const apszAllowedATOMFieldNamesWithSubElements[] = {
      40             :     "author", "contributor", nullptr};
      41             : 
      42             : static const char *const apszAllowedRSSFieldNames[] = {
      43             :     "title",          "link",          "description",
      44             :     "author",         "category",      "category_domain",
      45             :     "comments",       "enclosure_url", "enclosure_length",
      46             :     "enclosure_type", "guid",          "guid_isPermaLink",
      47             :     "pubDate",        "source",        "source_url",
      48             :     nullptr};
      49             : 
      50             : static const char *const apszAllowedATOMFieldNames[] = {"category_term",
      51             :                                                         "category_scheme",
      52             :                                                         "category_label",
      53             :                                                         "content",
      54             :                                                         "content_type",
      55             :                                                         "content_xml_lang",
      56             :                                                         "content_xml_base",
      57             :                                                         "summary",
      58             :                                                         "summary_type",
      59             :                                                         "summary_xml_lang",
      60             :                                                         "summary_xml_base",
      61             :                                                         "author_name",
      62             :                                                         "author_uri",
      63             :                                                         "author_email",
      64             :                                                         "contributor_name",
      65             :                                                         "contributor_uri",
      66             :                                                         "contributor_email",
      67             :                                                         "link_href",
      68             :                                                         "link_rel",
      69             :                                                         "link_type",
      70             :                                                         "link_length",
      71             :                                                         "id",
      72             :                                                         "published",
      73             :                                                         "rights",
      74             :                                                         "source",
      75             :                                                         "title",
      76             :                                                         "updated",
      77             :                                                         nullptr};
      78             : 
      79             : #define IS_LAT_ELEMENT(pszName)                                                \
      80             :     (STARTS_WITH(pszName, "geo:lat") || STARTS_WITH(pszName, "icbm:lat") ||    \
      81             :      STARTS_WITH(pszName, "geourl:lat"))
      82             : 
      83             : #define IS_LON_ELEMENT(pszName)                                                \
      84             :     (STARTS_WITH(pszName, "geo:lon") || STARTS_WITH(pszName, "icbm:lon") ||    \
      85             :      STARTS_WITH(pszName, "geourl:lon"))
      86             : 
      87             : #define IS_GEO_ELEMENT(pszName)                                                \
      88             :     (strcmp(pszName, "georss:point") == 0 ||                                   \
      89             :      strcmp(pszName, "georss:line") == 0 ||                                    \
      90             :      strcmp(pszName, "georss:box") == 0 ||                                     \
      91             :      strcmp(pszName, "georss:polygon") == 0 ||                                 \
      92             :      strcmp(pszName, "georss:where") == 0 || STARTS_WITH(pszName, "gml:") ||   \
      93             :      STARTS_WITH(pszName, "geo:") || STARTS_WITH(pszName, "icbm:") ||          \
      94             :      STARTS_WITH(pszName, "geourl:"))
      95             : 
      96             : /************************************************************************/
      97             : /*                           OGRGeoRSSLayer()                           */
      98             : /************************************************************************/
      99             : 
     100          68 : OGRGeoRSSLayer::OGRGeoRSSLayer(const char *pszFilename,
     101             :                                const char *pszLayerName,
     102             :                                OGRGeoRSSDataSource *poDS_,
     103          68 :                                OGRSpatialReference *poSRSIn, bool bWriteMode_)
     104          68 :     : poFeatureDefn(new OGRFeatureDefn(pszLayerName)), poSRS(poSRSIn),
     105          68 :       poDS(poDS_), eFormat(poDS_->GetFormat()), bWriteMode(bWriteMode_),
     106             :       nTotalFeatureCount(0), eof(false), nNextFID(0), fpGeoRSS(nullptr),
     107             :       bHasReadSchema(false),
     108             : #ifdef HAVE_EXPAT
     109             :       oParser(nullptr), oSchemaParser(nullptr),
     110             : #endif
     111             :       poGlobalGeom(nullptr), bStopParsing(false), bInFeature(false),
     112             :       hasFoundLat(false), hasFoundLon(false),
     113             : #ifdef HAVE_EXPAT
     114             :       latVal(0.0), lonVal(0.0),
     115             : #endif
     116             :       pszSubElementName(nullptr), pszSubElementValue(nullptr),
     117             :       nSubElementValueLen(0),
     118             : #ifdef HAVE_EXPAT
     119             :       iCurrentField(0),
     120             : #endif
     121             :       bInSimpleGeometry(false), bInGMLGeometry(false), bInGeoLat(false),
     122             :       bInGeoLong(false),
     123             : #ifdef HAVE_EXPAT
     124             :       bFoundGeom(false), bSameSRS(false),
     125             : #endif
     126             :       eGeomType(wkbUnknown), pszGMLSRSName(nullptr), bInTagWithSubTag(false),
     127             :       pszTagWithSubTag(nullptr), currentDepth(0), featureDepth(0),
     128             :       geometryDepth(0),
     129             : #ifdef HAVE_EXPAT
     130             :       currentFieldDefn(nullptr), nWithoutEventCounter(0),
     131             :       nDataHandlerCounter(0),
     132             : #endif
     133             :       setOfFoundFields(nullptr), poFeature(nullptr), ppoFeatureTab(nullptr),
     134         136 :       nFeatureTabLength(0), nFeatureTabIndex(0)
     135             : {
     136          68 :     SetDescription(poFeatureDefn->GetName());
     137          68 :     poFeatureDefn->Reference();
     138             : 
     139          68 :     if (poSRS)
     140             :     {
     141           1 :         poSRS->Reference();
     142           1 :         poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
     143             :     }
     144             : 
     145          68 :     if (!bWriteMode)
     146             :     {
     147          14 :         fpGeoRSS = VSIFOpenL(pszFilename, "r");
     148          14 :         if (fpGeoRSS == nullptr)
     149             :         {
     150           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s",
     151             :                      pszFilename);
     152           0 :             return;
     153             :         }
     154             :     }
     155             : 
     156          68 :     OGRGeoRSSLayer::ResetReading();
     157             : }
     158             : 
     159             : /************************************************************************/
     160             : /*                          ~OGRGeoRSSLayer()                           */
     161             : /************************************************************************/
     162             : 
     163         136 : OGRGeoRSSLayer::~OGRGeoRSSLayer()
     164             : 
     165             : {
     166             : #ifdef HAVE_EXPAT
     167          68 :     if (oParser)
     168          14 :         XML_ParserFree(oParser);
     169             : #endif
     170          68 :     poFeatureDefn->Release();
     171             : 
     172          68 :     if (poSRS)
     173          10 :         poSRS->Release();
     174             : 
     175          68 :     CPLFree(pszSubElementName);
     176          68 :     CPLFree(pszSubElementValue);
     177          68 :     CPLFree(pszGMLSRSName);
     178          68 :     CPLFree(pszTagWithSubTag);
     179          68 :     if (setOfFoundFields)
     180          13 :         CPLHashSetDestroy(setOfFoundFields);
     181          68 :     if (poGlobalGeom)
     182           0 :         delete poGlobalGeom;
     183             : 
     184          68 :     for (int i = nFeatureTabIndex; i < nFeatureTabLength; i++)
     185           0 :         delete ppoFeatureTab[i];
     186          68 :     CPLFree(ppoFeatureTab);
     187             : 
     188          68 :     if (poFeature)
     189           0 :         delete poFeature;
     190             : 
     191          68 :     if (fpGeoRSS)
     192          14 :         VSIFCloseL(fpGeoRSS);
     193         136 : }
     194             : 
     195             : /************************************************************************/
     196             : /*                            GetLayerDefn()                            */
     197             : /************************************************************************/
     198             : 
     199         348 : const OGRFeatureDefn *OGRGeoRSSLayer::GetLayerDefn() const
     200             : {
     201         348 :     if (!bHasReadSchema)
     202          63 :         const_cast<OGRGeoRSSLayer *>(this)->LoadSchema();
     203             : 
     204         348 :     return poFeatureDefn;
     205             : }
     206             : 
     207             : #ifdef HAVE_EXPAT
     208             : 
     209         304 : static void XMLCALL startElementCbk(void *pUserData, const char *pszName,
     210             :                                     const char **ppszAttr)
     211             : {
     212         304 :     ((OGRGeoRSSLayer *)pUserData)->startElementCbk(pszName, ppszAttr);
     213         304 : }
     214             : 
     215         304 : static void XMLCALL endElementCbk(void *pUserData, const char *pszName)
     216             : {
     217         304 :     ((OGRGeoRSSLayer *)pUserData)->endElementCbk(pszName);
     218         304 : }
     219             : 
     220         889 : static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen)
     221             : {
     222         889 :     ((OGRGeoRSSLayer *)pUserData)->dataHandlerCbk(data, nLen);
     223         889 : }
     224             : 
     225             : #endif
     226             : 
     227             : /************************************************************************/
     228             : /*                            ResetReading()                            */
     229             : /************************************************************************/
     230             : 
     231          84 : void OGRGeoRSSLayer::ResetReading()
     232             : 
     233             : {
     234          84 :     if (bWriteMode)
     235          70 :         return;
     236             : 
     237          14 :     eof = false;
     238          14 :     nNextFID = 0;
     239          14 :     if (fpGeoRSS)
     240             :     {
     241          14 :         VSIFSeekL(fpGeoRSS, 0, SEEK_SET);
     242          14 :         VSIFClearErrL(fpGeoRSS);
     243             : #ifdef HAVE_EXPAT
     244          14 :         if (oParser)
     245           0 :             XML_ParserFree(oParser);
     246             : 
     247          14 :         oParser = OGRCreateExpatXMLParser();
     248          14 :         XML_SetElementHandler(oParser, ::startElementCbk, ::endElementCbk);
     249          14 :         XML_SetCharacterDataHandler(oParser, ::dataHandlerCbk);
     250          14 :         XML_SetUserData(oParser, this);
     251             : #endif
     252             :     }
     253          14 :     bInFeature = false;
     254          14 :     hasFoundLat = false;
     255          14 :     hasFoundLon = false;
     256          14 :     bInSimpleGeometry = false;
     257          14 :     bInGMLGeometry = false;
     258          14 :     bInGeoLat = false;
     259          14 :     bInGeoLong = false;
     260          14 :     eGeomType = wkbUnknown;
     261          14 :     CPLFree(pszSubElementName);
     262          14 :     pszSubElementName = nullptr;
     263          14 :     CPLFree(pszSubElementValue);
     264          14 :     pszSubElementValue = nullptr;
     265          14 :     nSubElementValueLen = 0;
     266          14 :     CPLFree(pszGMLSRSName);
     267          14 :     pszGMLSRSName = nullptr;
     268             : 
     269          14 :     if (setOfFoundFields)
     270           0 :         CPLHashSetDestroy(setOfFoundFields);
     271          14 :     setOfFoundFields = nullptr;
     272             : 
     273          14 :     for (int i = nFeatureTabIndex; i < nFeatureTabLength; i++)
     274           0 :         delete ppoFeatureTab[i];
     275          14 :     CPLFree(ppoFeatureTab);
     276          14 :     nFeatureTabIndex = 0;
     277          14 :     nFeatureTabLength = 0;
     278          14 :     ppoFeatureTab = nullptr;
     279          14 :     if (poFeature)
     280           0 :         delete poFeature;
     281          14 :     poFeature = nullptr;
     282             : 
     283          14 :     currentDepth = 0;
     284          14 :     featureDepth = 0;
     285          14 :     geometryDepth = 0;
     286          14 :     bInTagWithSubTag = false;
     287          14 :     CPLFree(pszTagWithSubTag);
     288          14 :     pszTagWithSubTag = nullptr;
     289             : }
     290             : 
     291             : #ifdef HAVE_EXPAT
     292             : 
     293             : /************************************************************************/
     294             : /*                      AddStrToSubElementValue()                       */
     295             : /************************************************************************/
     296             : 
     297         211 : void OGRGeoRSSLayer::AddStrToSubElementValue(const char *pszStr)
     298             : {
     299         211 :     int len = static_cast<int>(strlen(pszStr));
     300             :     char *pszNewSubElementValue = static_cast<char *>(
     301         211 :         VSI_REALLOC_VERBOSE(pszSubElementValue, nSubElementValueLen + len + 1));
     302         211 :     if (pszNewSubElementValue == nullptr)
     303             :     {
     304           0 :         XML_StopParser(oParser, XML_FALSE);
     305           0 :         bStopParsing = true;
     306           0 :         return;
     307             :     }
     308         211 :     pszSubElementValue = pszNewSubElementValue;
     309             : 
     310         211 :     memcpy(pszSubElementValue + nSubElementValueLen, pszStr, len);
     311         211 :     nSubElementValueLen += len;
     312             : }
     313             : 
     314             : /************************************************************************/
     315             : /*                 OGRGeoRSS_GetOGRCompatibleTagName()                  */
     316             : /************************************************************************/
     317             : 
     318             : /** Replace ':' from XML NS element name by '_' more OGR friendly */
     319         346 : static char *OGRGeoRSS_GetOGRCompatibleTagName(const char *pszName)
     320             : {
     321         346 :     char *pszModName = CPLStrdup(pszName);
     322        3046 :     for (int i = 0; pszModName[i] != 0; i++)
     323             :     {
     324        2700 :         if (pszModName[i] == ':')
     325          28 :             pszModName[i] = '_';
     326             :     }
     327         346 :     return pszModName;
     328             : }
     329             : 
     330             : /************************************************************************/
     331             : /*                 OGRGeoRSSLayerATOMTagHasSubElement()                 */
     332             : /************************************************************************/
     333             : 
     334         120 : static bool OGRGeoRSSLayerATOMTagHasSubElement(const char *pszName)
     335             : {
     336         312 :     for (unsigned int i = 0;
     337         312 :          apszAllowedATOMFieldNamesWithSubElements[i] != nullptr; i++)
     338             :     {
     339         228 :         if (strcmp(pszName, apszAllowedATOMFieldNamesWithSubElements[i]) == 0)
     340          36 :             return true;
     341             :     }
     342          84 :     return false;
     343             : }
     344             : 
     345             : /************************************************************************/
     346             : /*                          startElementCbk()                           */
     347             : /************************************************************************/
     348             : 
     349         304 : void OGRGeoRSSLayer::startElementCbk(const char *pszName, const char **ppszAttr)
     350             : {
     351         304 :     bool bSerializeTag = false;
     352         304 :     const char *pszNoNSName = pszName;
     353         304 :     const char *pszColon = strchr(pszNoNSName, ':');
     354         304 :     if (pszColon)
     355          77 :         pszNoNSName = pszColon + 1;
     356             : 
     357         304 :     if (bStopParsing)
     358           0 :         return;
     359             : 
     360         304 :     if ((eFormat == GEORSS_ATOM && currentDepth == 1 &&
     361          23 :          strcmp(pszNoNSName, "entry") == 0) ||
     362         301 :         ((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) && !bInFeature &&
     363          68 :          (currentDepth == 1 || currentDepth == 2) &&
     364          58 :          strcmp(pszNoNSName, "item") == 0))
     365             :     {
     366          28 :         featureDepth = currentDepth;
     367             : 
     368          28 :         if (poFeature)
     369           0 :             delete poFeature;
     370             : 
     371          28 :         poFeature = new OGRFeature(poFeatureDefn);
     372          28 :         poFeature->SetFID(nNextFID++);
     373             : 
     374          28 :         bInFeature = true;
     375          28 :         hasFoundLat = false;
     376          28 :         hasFoundLon = false;
     377          28 :         bInSimpleGeometry = false;
     378          28 :         bInGMLGeometry = false;
     379          28 :         bInGeoLat = false;
     380          28 :         bInGeoLong = false;
     381          28 :         eGeomType = wkbUnknown;
     382          28 :         geometryDepth = 0;
     383          28 :         bInTagWithSubTag = false;
     384             : 
     385          28 :         if (setOfFoundFields)
     386          15 :             CPLHashSetDestroy(setOfFoundFields);
     387          28 :         setOfFoundFields =
     388          28 :             CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, CPLFree);
     389             :     }
     390         276 :     else if (bInFeature && bInTagWithSubTag && currentDepth == 3)
     391             :     {
     392             :         char *pszFieldName =
     393          15 :             CPLStrdup(CPLSPrintf("%s_%s", pszTagWithSubTag, pszNoNSName));
     394             : 
     395          15 :         CPLFree(pszSubElementName);
     396          15 :         pszSubElementName = nullptr;
     397          15 :         CPLFree(pszSubElementValue);
     398          15 :         pszSubElementValue = nullptr;
     399          15 :         nSubElementValueLen = 0;
     400             : 
     401          15 :         iCurrentField = poFeatureDefn->GetFieldIndex(pszFieldName);
     402          15 :         if (iCurrentField >= 0)
     403          15 :             pszSubElementName = CPLStrdup(pszFieldName);
     404             : 
     405          15 :         CPLFree(pszFieldName);
     406             :     }
     407         291 :     else if (bInFeature && eFormat == GEORSS_ATOM && currentDepth == 2 &&
     408          30 :              OGRGeoRSSLayerATOMTagHasSubElement(pszNoNSName))
     409             :     {
     410           9 :         CPLFree(pszTagWithSubTag);
     411           9 :         pszTagWithSubTag = CPLStrdup(pszNoNSName);
     412             : 
     413           9 :         int count = 1;
     414          12 :         while (CPLHashSetLookup(setOfFoundFields, pszTagWithSubTag) != nullptr)
     415             :         {
     416           3 :             count++;
     417           3 :             CPLFree(pszTagWithSubTag);
     418           3 :             pszTagWithSubTag =
     419           3 :                 CPLStrdup(CPLSPrintf("%s%d", pszNoNSName, count));
     420             :         }
     421           9 :         CPLHashSetInsert(setOfFoundFields, CPLStrdup(pszTagWithSubTag));
     422             : 
     423           9 :         bInTagWithSubTag = true;
     424             :     }
     425         252 :     else if (bInGMLGeometry)
     426             :     {
     427          17 :         bSerializeTag = true;
     428             :     }
     429         235 :     else if (bInSimpleGeometry || bInGeoLat || bInGeoLong)
     430             :     {
     431             :         /* Should not happen for a valid document. */
     432             :     }
     433         235 :     else if (IS_LAT_ELEMENT(pszName))
     434             :     {
     435           1 :         CPLFree(pszSubElementValue);
     436           1 :         pszSubElementValue = nullptr;
     437           1 :         nSubElementValueLen = 0;
     438           1 :         bInGeoLat = true;
     439             :     }
     440         234 :     else if (IS_LON_ELEMENT(pszName))
     441             :     {
     442           1 :         CPLFree(pszSubElementValue);
     443           1 :         pszSubElementValue = nullptr;
     444           1 :         nSubElementValueLen = 0;
     445           1 :         bInGeoLong = true;
     446             :     }
     447         233 :     else if (strcmp(pszName, "georss:point") == 0 ||
     448         230 :              strcmp(pszName, "georss:line") == 0 ||
     449         228 :              strcmp(pszName, "geo:line") == 0 ||
     450         228 :              strcmp(pszName, "georss:polygon") == 0 ||
     451         225 :              strcmp(pszName, "georss:box") == 0)
     452             :     {
     453          10 :         CPLFree(pszSubElementValue);
     454          10 :         pszSubElementValue = nullptr;
     455          10 :         nSubElementValueLen = 0;
     456          17 :         eGeomType = strcmp(pszName, "georss:point") == 0 ? wkbPoint
     457          12 :                     : (strcmp(pszName, "georss:line") == 0 ||
     458           5 :                        strcmp(pszName, "geo:line") == 0)
     459          12 :                         ? wkbLineString
     460           7 :                     : (strcmp(pszName, "georss:polygon") == 0 ||
     461           2 :                        strcmp(pszName, "georss:box") == 0)
     462           7 :                         ? wkbPolygon
     463             :                         : wkbUnknown;
     464          10 :         bInSimpleGeometry = true;
     465          10 :         geometryDepth = currentDepth;
     466             :     }
     467         223 :     else if (strcmp(pszName, "gml:Point") == 0 ||
     468         220 :              strcmp(pszName, "gml:LineString") == 0 ||
     469         217 :              strcmp(pszName, "gml:Polygon") == 0 ||
     470         214 :              strcmp(pszName, "gml:MultiPoint") == 0 ||
     471         214 :              strcmp(pszName, "gml:MultiLineString") == 0 ||
     472         214 :              strcmp(pszName, "gml:MultiPolygon") == 0 ||
     473         214 :              strcmp(pszName, "gml:Envelope") == 0)
     474             :     {
     475          10 :         CPLFree(pszSubElementValue);
     476          10 :         pszSubElementValue = nullptr;
     477          10 :         nSubElementValueLen = 0;
     478          10 :         AddStrToSubElementValue(CPLSPrintf("<%s>", pszName));
     479          10 :         bInGMLGeometry = true;
     480          10 :         geometryDepth = currentDepth;
     481          10 :         CPLFree(pszGMLSRSName);
     482          10 :         pszGMLSRSName = nullptr;
     483          11 :         for (int i = 0; ppszAttr[i]; i += 2)
     484             :         {
     485           1 :             if (strcmp(ppszAttr[i], "srsName") == 0)
     486             :             {
     487           1 :                 if (pszGMLSRSName == nullptr)
     488           1 :                     pszGMLSRSName = CPLStrdup(ppszAttr[i + 1]);
     489             :             }
     490          10 :         }
     491             :     }
     492         213 :     else if (bInFeature && currentDepth == featureDepth + 1)
     493             :     {
     494         137 :         CPLFree(pszSubElementName);
     495         137 :         pszSubElementName = nullptr;
     496         137 :         CPLFree(pszSubElementValue);
     497         137 :         pszSubElementValue = nullptr;
     498         137 :         nSubElementValueLen = 0;
     499         137 :         iCurrentField = -1;
     500             : 
     501         137 :         if (pszName != pszNoNSName && STARTS_WITH(pszName, "atom:"))
     502           7 :             pszName = pszNoNSName;
     503             : 
     504         137 :         pszSubElementName = CPLStrdup(pszName);
     505         137 :         int count = 1;
     506         145 :         while (CPLHashSetLookup(setOfFoundFields, pszSubElementName) != nullptr)
     507             :         {
     508           8 :             count++;
     509           8 :             if (count == 100)
     510             :             {
     511           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     512             :                          "Too many repeated fields");
     513           0 :                 CPLFree(pszSubElementName);
     514           0 :                 pszSubElementName = nullptr;
     515           0 :                 break;
     516             :             }
     517           8 :             CPLFree(pszSubElementName);
     518           8 :             pszSubElementName = CPLStrdup(CPLSPrintf("%s%d", pszName, count));
     519             :         }
     520         137 :         if (pszSubElementName)
     521             :         {
     522         137 :             CPLHashSetInsert(setOfFoundFields, CPLStrdup(pszSubElementName));
     523             : 
     524             :             char *pszCompatibleName =
     525         137 :                 OGRGeoRSS_GetOGRCompatibleTagName(pszSubElementName);
     526         137 :             iCurrentField = poFeatureDefn->GetFieldIndex(pszCompatibleName);
     527         137 :             CPLFree(pszSubElementName);
     528             : 
     529         137 :             for (int i = 0;
     530         178 :                  ppszAttr[i] != nullptr && ppszAttr[i + 1] != nullptr; i += 2)
     531             :             {
     532          41 :                 char *pszAttrCompatibleName = OGRGeoRSS_GetOGRCompatibleTagName(
     533          41 :                     CPLSPrintf("%s_%s", pszCompatibleName, ppszAttr[i]));
     534             :                 const int iAttrField =
     535          41 :                     poFeatureDefn->GetFieldIndex(pszAttrCompatibleName);
     536          41 :                 if (iAttrField >= 0)
     537             :                 {
     538          41 :                     if (poFeatureDefn->GetFieldDefn(iAttrField)->GetType() ==
     539             :                         OFTReal)
     540           0 :                         poFeature->SetField(iAttrField,
     541           0 :                                             CPLAtof(ppszAttr[i + 1]));
     542             :                     else
     543          41 :                         poFeature->SetField(iAttrField, ppszAttr[i + 1]);
     544             :                 }
     545          41 :                 CPLFree(pszAttrCompatibleName);
     546             :             }
     547             : 
     548         137 :             if (iCurrentField < 0)
     549             :             {
     550          16 :                 pszSubElementName = nullptr;
     551             :             }
     552             :             else
     553             :             {
     554         121 :                 pszSubElementName = CPLStrdup(pszCompatibleName);
     555             :             }
     556         137 :             CPLFree(pszCompatibleName);
     557         137 :         }
     558             :     }
     559          76 :     else if (bInFeature && currentDepth > featureDepth + 1 &&
     560           9 :              pszSubElementName != nullptr)
     561             :     {
     562           9 :         bSerializeTag = true;
     563             :     }
     564             : 
     565         304 :     if (bSerializeTag)
     566             :     {
     567          26 :         AddStrToSubElementValue("<");
     568          26 :         AddStrToSubElementValue(pszName);
     569          29 :         for (int i = 0; ppszAttr[i] != nullptr && ppszAttr[i + 1] != nullptr;
     570           3 :              i += 2)
     571             :         {
     572           3 :             AddStrToSubElementValue(" ");
     573           3 :             AddStrToSubElementValue(ppszAttr[i]);
     574           3 :             AddStrToSubElementValue("=\"");
     575           3 :             AddStrToSubElementValue(ppszAttr[i + 1]);
     576           3 :             AddStrToSubElementValue("\"");
     577             :         }
     578          26 :         AddStrToSubElementValue(">");
     579             :     }
     580             : 
     581         304 :     currentDepth++;
     582             : }
     583             : 
     584             : /************************************************************************/
     585             : /*             OGRGeoRSSLayerTrimLeadingAndTrailingSpaces()             */
     586             : /************************************************************************/
     587             : 
     588          10 : static void OGRGeoRSSLayerTrimLeadingAndTrailingSpaces(char *pszStr)
     589             : {
     590             :     // Trim leading spaces, tabs and newlines.
     591          10 :     int i = 0;
     592          13 :     while (pszStr[i] != '\0' &&
     593          13 :            (pszStr[i] == ' ' || pszStr[i] == '\t' || pszStr[i] == '\n'))
     594           3 :         i++;
     595          10 :     memmove(pszStr, pszStr + i, strlen(pszStr + i) + 1);
     596             : 
     597             :     // Trim trailing spaces, tabs and newlines.
     598          10 :     i = static_cast<int>(strlen(pszStr)) - 1;
     599          13 :     while (i >= 0 &&
     600          13 :            (pszStr[i] == ' ' || pszStr[i] == '\t' || pszStr[i] == '\n'))
     601             :     {
     602           3 :         pszStr[i] = '\0';
     603           3 :         i--;
     604             :     }
     605          10 : }
     606             : 
     607             : /************************************************************************/
     608             : /*                           endElementCbk()                            */
     609             : /************************************************************************/
     610             : 
     611         304 : void OGRGeoRSSLayer::endElementCbk(const char *pszName)
     612             : {
     613         304 :     OGRGeometry *poGeom = nullptr;
     614             : 
     615         304 :     if (bStopParsing)
     616           0 :         return;
     617             : 
     618         304 :     currentDepth--;
     619         304 :     const char *pszNoNSName = pszName;
     620         304 :     const char *pszColon = strchr(pszNoNSName, ':');
     621         304 :     if (pszColon)
     622          77 :         pszNoNSName = pszColon + 1;
     623             : 
     624         304 :     if (bInFeature && currentDepth == featureDepth)
     625             :     {
     626          28 :         bInFeature = false;
     627          28 :         bInTagWithSubTag = false;
     628             : 
     629          28 :         if (hasFoundLat && hasFoundLon)
     630           1 :             poFeature->SetGeometryDirectly(new OGRPoint(lonVal, latVal));
     631          36 :         else if (poFeature->GetGeometryRef() == nullptr &&
     632           9 :                  poGlobalGeom != nullptr)
     633           0 :             poFeature->SetGeometry(poGlobalGeom);
     634             : 
     635          28 :         hasFoundLat = false;
     636          28 :         hasFoundLon = false;
     637             : 
     638          28 :         if (poSRS != nullptr && poFeature->GetGeometryRef() != nullptr)
     639          19 :             poFeature->GetGeometryRef()->assignSpatialReference(poSRS);
     640             : 
     641          56 :         if ((m_poFilterGeom == nullptr ||
     642          56 :              FilterGeometry(poFeature->GetGeometryRef())) &&
     643          28 :             (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
     644             :         {
     645          56 :             ppoFeatureTab = static_cast<OGRFeature **>(CPLRealloc(
     646          28 :                 ppoFeatureTab, sizeof(OGRFeature *) * (nFeatureTabLength + 1)));
     647          28 :             ppoFeatureTab[nFeatureTabLength] = poFeature;
     648          28 :             nFeatureTabLength++;
     649             :         }
     650             :         else
     651             :         {
     652           0 :             delete poFeature;
     653             :         }
     654          28 :         poFeature = nullptr;
     655          28 :         return;
     656             :     }
     657             : 
     658         276 :     if (bInTagWithSubTag && currentDepth == 3)
     659             :     {
     660             :         char *pszFieldName =
     661          15 :             CPLStrdup(CPLSPrintf("%s_%s", pszTagWithSubTag, pszNoNSName));
     662             : 
     663          15 :         if (iCurrentField != -1 && pszSubElementName &&
     664          15 :             strcmp(pszFieldName, pszSubElementName) == 0 && poFeature &&
     665          15 :             pszSubElementValue && nSubElementValueLen)
     666             :         {
     667          15 :             pszSubElementValue[nSubElementValueLen] = 0;
     668          15 :             if (poFeatureDefn->GetFieldDefn(iCurrentField)->GetType() ==
     669             :                 OFTReal)
     670           0 :                 poFeature->SetField(iCurrentField, CPLAtof(pszSubElementValue));
     671             :             else
     672          15 :                 poFeature->SetField(iCurrentField, pszSubElementValue);
     673             :         }
     674             : 
     675          15 :         CPLFree(pszSubElementName);
     676          15 :         pszSubElementName = nullptr;
     677          15 :         CPLFree(pszSubElementValue);
     678          15 :         pszSubElementValue = nullptr;
     679          15 :         nSubElementValueLen = 0;
     680             : 
     681          15 :         CPLFree(pszFieldName);
     682             :     }
     683         291 :     else if (bInFeature && eFormat == GEORSS_ATOM && currentDepth == 2 &&
     684          30 :              OGRGeoRSSLayerATOMTagHasSubElement(pszNoNSName))
     685             :     {
     686           9 :         bInTagWithSubTag = false;
     687             :     }
     688         252 :     else if (bInGMLGeometry)
     689             :     {
     690          27 :         AddStrToSubElementValue("</");
     691          27 :         AddStrToSubElementValue(pszName);
     692          27 :         AddStrToSubElementValue(">");
     693          27 :         if (currentDepth > geometryDepth)
     694             :         {
     695             :         }
     696             :         else
     697             :         {
     698          10 :             pszSubElementValue[nSubElementValueLen] = 0;
     699          10 :             CPLAssert(STARTS_WITH(pszName, "gml:"));
     700          10 :             poGeom = (OGRGeometry *)OGR_G_CreateFromGML(pszSubElementValue);
     701             : 
     702          10 :             if (poGeom != nullptr && !poGeom->IsEmpty())
     703             :             {
     704           9 :                 bool bSwapCoordinates = false;
     705           9 :                 if (pszGMLSRSName)
     706             :                 {
     707             :                     OGRSpatialReference *poSRSFeature =
     708           1 :                         new OGRSpatialReference();
     709           1 :                     poSRSFeature->importFromURN(pszGMLSRSName);
     710           1 :                     poGeom->assignSpatialReference(poSRSFeature);
     711           1 :                     poSRSFeature->Release();
     712             :                 }
     713             :                 else
     714             :                 {
     715           8 :                     bSwapCoordinates = true; /* lat, lon WGS 84 */
     716             :                 }
     717             : 
     718           9 :                 if (bSwapCoordinates)
     719             :                 {
     720           8 :                     poGeom->swapXY();
     721             :                 }
     722             :             }
     723          10 :             bInGMLGeometry = false;
     724             :         }
     725             :     }
     726         225 :     else if (bInSimpleGeometry)
     727             :     {
     728          10 :         if (currentDepth > geometryDepth)
     729             :         {
     730             :             // Should not happen for a valid document.
     731             :         }
     732             :         else
     733             :         {
     734          10 :             if (pszSubElementValue)
     735             :             {
     736          10 :                 pszSubElementValue[nSubElementValueLen] = 0;
     737             : 
     738             :                 // Trim any leading and trailing spaces, tabs, newlines, etc.
     739          10 :                 OGRGeoRSSLayerTrimLeadingAndTrailingSpaces(pszSubElementValue);
     740             : 
     741             :                 // Caution: Order is latitude, longitude.
     742          20 :                 char **papszTokens = CSLTokenizeStringComplex(
     743          10 :                     pszSubElementValue, " ,", TRUE, FALSE);
     744             : 
     745          10 :                 const int nTokens = CSLCount(papszTokens);
     746          10 :                 if ((nTokens % 2) != 0 ||
     747           9 :                     (eGeomType == wkbPoint && nTokens != 2) ||
     748           9 :                     (eGeomType == wkbLineString && nTokens < 4) ||
     749           9 :                     (strcmp(pszName, "georss:polygon") == 0 && nTokens < 6) ||
     750           9 :                     (strcmp(pszName, "georss:box") == 0 && nTokens != 4))
     751             :                 {
     752           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
     753             :                              "Wrong number of coordinates in %s",
     754             :                              pszSubElementValue);
     755             :                 }
     756           9 :                 else if (eGeomType == wkbPoint)
     757             :                 {
     758           6 :                     poGeom = new OGRPoint(CPLAtof(papszTokens[1]),
     759           3 :                                           CPLAtof(papszTokens[0]));
     760             :                 }
     761           6 :                 else if (eGeomType == wkbLineString)
     762             :                 {
     763           2 :                     OGRLineString *poLineString = new OGRLineString();
     764           2 :                     poGeom = poLineString;
     765           8 :                     for (int i = 0; i < nTokens; i += 2)
     766             :                     {
     767           6 :                         poLineString->addPoint(CPLAtof(papszTokens[i + 1]),
     768           6 :                                                CPLAtof(papszTokens[i]));
     769             :                     }
     770             :                 }
     771           4 :                 else if (eGeomType == wkbPolygon)
     772             :                 {
     773           4 :                     OGRPolygon *poPolygon = new OGRPolygon();
     774           4 :                     OGRLinearRing *poLinearRing = new OGRLinearRing();
     775           4 :                     poGeom = poPolygon;
     776           4 :                     poPolygon->addRingDirectly(poLinearRing);
     777           4 :                     if (strcmp(pszName, "georss:polygon") == 0)
     778             :                     {
     779          18 :                         for (int i = 0; i < nTokens; i += 2)
     780             :                         {
     781          15 :                             poLinearRing->addPoint(CPLAtof(papszTokens[i + 1]),
     782          15 :                                                    CPLAtof(papszTokens[i]));
     783             :                         }
     784             :                     }
     785             :                     else
     786             :                     {
     787           1 :                         const double lat1 = CPLAtof(papszTokens[0]);
     788           1 :                         const double lon1 = CPLAtof(papszTokens[1]);
     789           1 :                         const double lat2 = CPLAtof(papszTokens[2]);
     790           1 :                         const double lon2 = CPLAtof(papszTokens[3]);
     791           1 :                         poLinearRing->addPoint(lon1, lat1);
     792           1 :                         poLinearRing->addPoint(lon1, lat2);
     793           1 :                         poLinearRing->addPoint(lon2, lat2);
     794           1 :                         poLinearRing->addPoint(lon2, lat1);
     795           1 :                         poLinearRing->addPoint(lon1, lat1);
     796             :                     }
     797             :                 }
     798             : 
     799          10 :                 CSLDestroy(papszTokens);
     800             :             }
     801          10 :             bInSimpleGeometry = false;
     802             :         }
     803             :     }
     804         215 :     else if (IS_LAT_ELEMENT(pszName))
     805             :     {
     806           1 :         if (pszSubElementValue)
     807             :         {
     808           1 :             hasFoundLat = true;
     809           1 :             pszSubElementValue[nSubElementValueLen] = 0;
     810           1 :             latVal = CPLAtof(pszSubElementValue);
     811             :         }
     812           1 :         bInGeoLat = false;
     813             :     }
     814         214 :     else if (IS_LON_ELEMENT(pszName))
     815             :     {
     816           1 :         if (pszSubElementValue)
     817             :         {
     818           1 :             hasFoundLon = true;
     819           1 :             pszSubElementValue[nSubElementValueLen] = 0;
     820           1 :             lonVal = CPLAtof(pszSubElementValue);
     821             :         }
     822           1 :         bInGeoLong = false;
     823             :     }
     824         213 :     else if (bInFeature && currentDepth == featureDepth + 1)
     825             :     {
     826         137 :         if (iCurrentField != -1 && pszSubElementName && poFeature &&
     827         121 :             pszSubElementValue && nSubElementValueLen)
     828             :         {
     829         121 :             pszSubElementValue[nSubElementValueLen] = 0;
     830         121 :             if (poFeatureDefn->GetFieldDefn(iCurrentField)->GetType() ==
     831             :                 OFTDateTime)
     832             :             {
     833             :                 OGRField sField;
     834          27 :                 if (OGRParseRFC822DateTime(pszSubElementValue, &sField))
     835             :                 {
     836          21 :                     poFeature->SetField(iCurrentField, &sField);
     837             :                 }
     838           6 :                 else if (OGRParseXMLDateTime(pszSubElementValue, &sField))
     839             :                 {
     840           6 :                     poFeature->SetField(iCurrentField, &sField);
     841             :                 }
     842             :                 else
     843             :                 {
     844           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     845             :                              "Could not parse %s as a valid dateTime",
     846             :                              pszSubElementValue);
     847             :                 }
     848             :             }
     849             :             else
     850             :             {
     851          94 :                 if (poFeatureDefn->GetFieldDefn(iCurrentField)->GetType() ==
     852             :                     OFTReal)
     853           0 :                     poFeature->SetField(iCurrentField,
     854           0 :                                         CPLAtof(pszSubElementValue));
     855             :                 else
     856          94 :                     poFeature->SetField(iCurrentField, pszSubElementValue);
     857             :             }
     858             :         }
     859             : 
     860         137 :         CPLFree(pszSubElementName);
     861         137 :         pszSubElementName = nullptr;
     862         137 :         CPLFree(pszSubElementValue);
     863         137 :         pszSubElementValue = nullptr;
     864         137 :         nSubElementValueLen = 0;
     865             :     }
     866          76 :     else if (bInFeature && currentDepth > featureDepth + 1 &&
     867           9 :              pszSubElementName != nullptr)
     868             :     {
     869           9 :         AddStrToSubElementValue("</");
     870           9 :         AddStrToSubElementValue(pszName);
     871           9 :         AddStrToSubElementValue(">");
     872             :     }
     873             : 
     874         276 :     if (poGeom != nullptr)
     875             :     {
     876          18 :         if (poFeature != nullptr)
     877             :         {
     878          18 :             poFeature->SetGeometryDirectly(poGeom);
     879             :         }
     880           0 :         else if (!bInFeature)
     881             :         {
     882           0 :             if (poGlobalGeom != nullptr)
     883           0 :                 delete poGlobalGeom;
     884           0 :             poGlobalGeom = poGeom;
     885             :         }
     886             :         else
     887             :         {
     888           0 :             delete poGeom;
     889             :         }
     890             :     }
     891         258 :     else if (!bInFeature && hasFoundLat && hasFoundLon)
     892             :     {
     893           0 :         if (poGlobalGeom != nullptr)
     894           0 :             delete poGlobalGeom;
     895           0 :         poGlobalGeom = new OGRPoint(lonVal, latVal);
     896           0 :         hasFoundLat = false;
     897           0 :         hasFoundLon = false;
     898             :     }
     899             : }
     900             : 
     901             : /************************************************************************/
     902             : /*                           dataHandlerCbk()                           */
     903             : /************************************************************************/
     904             : 
     905         889 : void OGRGeoRSSLayer::dataHandlerCbk(const char *data, int nLen)
     906             : {
     907         889 :     if (bStopParsing)
     908           0 :         return;
     909             : 
     910         889 :     if (bInGMLGeometry || bInSimpleGeometry || bInGeoLat || bInGeoLong ||
     911         834 :         pszSubElementName != nullptr)
     912             :     {
     913         208 :         char *pszNewSubElementValue = static_cast<char *>(VSI_REALLOC_VERBOSE(
     914             :             pszSubElementValue, nSubElementValueLen + nLen + 1));
     915         208 :         if (pszNewSubElementValue == nullptr)
     916             :         {
     917           0 :             XML_StopParser(oSchemaParser, XML_FALSE);
     918           0 :             bStopParsing = true;
     919           0 :             return;
     920             :         }
     921         208 :         pszSubElementValue = pszNewSubElementValue;
     922         208 :         memcpy(pszSubElementValue + nSubElementValueLen, data, nLen);
     923         208 :         nSubElementValueLen += nLen;
     924             :     }
     925             : }
     926             : #endif
     927             : 
     928             : /************************************************************************/
     929             : /*                           GetNextFeature()                           */
     930             : /************************************************************************/
     931             : 
     932          44 : OGRFeature *OGRGeoRSSLayer::GetNextFeature()
     933             : {
     934          44 :     if (bWriteMode)
     935             :     {
     936          16 :         CPLError(CE_Failure, CPLE_NotSupported,
     937             :                  "Cannot read features when writing a GeoRSS file");
     938          16 :         return nullptr;
     939             :     }
     940             : 
     941          28 :     if (fpGeoRSS == nullptr)
     942           0 :         return nullptr;
     943             : 
     944          28 :     if (!bHasReadSchema)
     945           4 :         LoadSchema();
     946             : 
     947          28 :     if (bStopParsing)
     948           0 :         return nullptr;
     949             : 
     950             : #ifdef HAVE_EXPAT
     951          28 :     if (nFeatureTabIndex < nFeatureTabLength)
     952             :     {
     953          15 :         return ppoFeatureTab[nFeatureTabIndex++];
     954             :     }
     955             : 
     956          13 :     if (VSIFEofL(fpGeoRSS) || VSIFErrorL(fpGeoRSS))
     957           0 :         return nullptr;
     958             : 
     959          13 :     CPLFree(ppoFeatureTab);
     960          13 :     ppoFeatureTab = nullptr;
     961          13 :     this->nFeatureTabLength = 0;
     962          13 :     nFeatureTabIndex = 0;
     963             : 
     964          13 :     int nDone = 0;
     965          26 :     std::vector<char> aBuf(PARSER_BUF_SIZE);
     966           0 :     do
     967             :     {
     968             :         unsigned int nLen = static_cast<unsigned int>(
     969          13 :             VSIFReadL(aBuf.data(), 1, aBuf.size(), fpGeoRSS));
     970          13 :         nDone = nLen < aBuf.size();
     971          13 :         if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
     972             :         {
     973           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     974             :                      "XML parsing of GeoRSS file failed: %s",
     975           0 :                      OGRExpatGetParseError(oParser));
     976           0 :             bStopParsing = true;
     977             :         }
     978          13 :     } while (!nDone && !bStopParsing && this->nFeatureTabLength == 0);
     979             : 
     980          13 :     return this->nFeatureTabLength ? ppoFeatureTab[nFeatureTabIndex++]
     981          13 :                                    : nullptr;
     982             : #else
     983             :     return nullptr;
     984             : #endif
     985             : }
     986             : 
     987             : /************************************************************************/
     988             : /*               OGRGeoRSSLayerIsStandardFieldInternal()                */
     989             : /************************************************************************/
     990             : 
     991         187 : static bool OGRGeoRSSLayerIsStandardFieldInternal(const char *pszName,
     992             :                                                   const char *const *papszNames)
     993             : {
     994        2223 :     for (unsigned int i = 0; papszNames[i] != nullptr; i++)
     995             :     {
     996        2121 :         if (strcmp(pszName, papszNames[i]) == 0)
     997             :         {
     998          74 :             return true;
     999             :         }
    1000             : 
    1001        2047 :         const char *pszUnderscore = strchr(papszNames[i], '_');
    1002        2047 :         if (pszUnderscore == nullptr)
    1003             :         {
    1004        1115 :             size_t nLen = strlen(papszNames[i]);
    1005        1115 :             if (strncmp(pszName, papszNames[i], nLen) == 0)
    1006             :             {
    1007          12 :                 size_t k = nLen;
    1008          18 :                 while (pszName[k] >= '0' && pszName[k] <= '9')
    1009           6 :                     k++;
    1010          12 :                 if (pszName[k] == '\0')
    1011           3 :                     return true;
    1012             :             }
    1013             :         }
    1014             :         else
    1015             :         {
    1016         932 :             const size_t nLen =
    1017         932 :                 static_cast<size_t>(pszUnderscore - papszNames[i]);
    1018         932 :             if (strncmp(pszName, papszNames[i], nLen) == 0)
    1019             :             {
    1020          23 :                 size_t k = nLen;
    1021          37 :                 while (pszName[k] >= '0' && pszName[k] <= '9')
    1022          14 :                     k++;
    1023          23 :                 if (pszName[k] == '_' &&
    1024          23 :                     strcmp(pszName + k, pszUnderscore) == 0)
    1025           8 :                     return true;
    1026             :             }
    1027             :         }
    1028             :     }
    1029         102 :     return false;
    1030             : }
    1031             : 
    1032             : /************************************************************************/
    1033             : /*                  OGRGeoRSSLayer::IsStandardField()                   */
    1034             : /************************************************************************/
    1035             : 
    1036         187 : bool OGRGeoRSSLayer::IsStandardField(const char *pszName)
    1037             : {
    1038         187 :     if (eFormat == GEORSS_RSS)
    1039             :     {
    1040         165 :         return OGRGeoRSSLayerIsStandardFieldInternal(pszName,
    1041         165 :                                                      apszAllowedRSSFieldNames);
    1042             :     }
    1043             :     else
    1044             :     {
    1045          22 :         return OGRGeoRSSLayerIsStandardFieldInternal(pszName,
    1046          22 :                                                      apszAllowedATOMFieldNames);
    1047             :     }
    1048             : }
    1049             : 
    1050             : /************************************************************************/
    1051             : /*                  OGRGeoRSSLayerSplitComposedField()                  */
    1052             : /************************************************************************/
    1053             : 
    1054         103 : static void OGRGeoRSSLayerSplitComposedField(const char *pszName,
    1055             :                                              std::string &osElementName,
    1056             :                                              std::string &osNumber,
    1057             :                                              std::string &osAttributeName)
    1058             : {
    1059         103 :     osElementName = pszName;
    1060             : 
    1061         103 :     int i = 0;
    1062         751 :     while (pszName[i] != '\0' && pszName[i] != '_' &&
    1063         663 :            !(pszName[i] >= '0' && pszName[i] <= '9'))
    1064             :     {
    1065         648 :         i++;
    1066             :     }
    1067             : 
    1068         103 :     osElementName.resize(i);
    1069             : 
    1070         103 :     if (pszName[i] >= '0' && pszName[i] <= '9')
    1071             :     {
    1072          15 :         osNumber = pszName + i;
    1073          15 :         const auto nPos = osNumber.find('_');
    1074          15 :         if (nPos != std::string::npos)
    1075             :         {
    1076          11 :             osAttributeName = osNumber.substr(nPos + 1);
    1077          11 :             osNumber.resize(nPos);
    1078             :         }
    1079             :         else
    1080             :         {
    1081           4 :             osAttributeName.clear();
    1082          15 :         }
    1083             :     }
    1084             :     else
    1085             :     {
    1086          88 :         osNumber.clear();
    1087          88 :         if (pszName[i] == '_')
    1088             :         {
    1089          29 :             osAttributeName = pszName + i + 1;
    1090             :         }
    1091             :         else
    1092             :         {
    1093          59 :             osAttributeName.clear();
    1094             :         }
    1095             :     }
    1096         103 : }
    1097             : 
    1098             : /************************************************************************/
    1099             : /*                  OGRGeoRSSLayerWriteSimpleElement()                  */
    1100             : /************************************************************************/
    1101             : 
    1102           8 : static void OGRGeoRSSLayerWriteSimpleElement(VSILFILE *fp,
    1103             :                                              const char *pszElementName,
    1104             :                                              const char *pszNumber,
    1105             :                                              const char *const *papszNames,
    1106             :                                              OGRFeatureDefn *poFeatureDefn,
    1107             :                                              OGRFeature *poFeature)
    1108             : {
    1109           8 :     VSIFPrintfL(fp, "      <%s", pszElementName);
    1110             : 
    1111         152 :     for (unsigned int k = 0; papszNames[k] != nullptr; k++)
    1112             :     {
    1113         144 :         if (strncmp(papszNames[k], pszElementName, strlen(pszElementName)) ==
    1114          20 :                 0 &&
    1115          20 :             papszNames[k][strlen(pszElementName)] == '_')
    1116             :         {
    1117          14 :             const char *pszAttributeName =
    1118          14 :                 papszNames[k] + strlen(pszElementName) + 1;
    1119          14 :             char *pszFieldName = CPLStrdup(CPLSPrintf(
    1120             :                 "%s%s_%s", pszElementName, pszNumber, pszAttributeName));
    1121          14 :             int iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
    1122          14 :             if (iIndex != -1 && poFeature->IsFieldSetAndNotNull(iIndex))
    1123             :             {
    1124          13 :                 char *pszValue = OGRGetXML_UTF8_EscapedString(
    1125             :                     poFeature->GetFieldAsString(iIndex));
    1126          13 :                 VSIFPrintfL(fp, " %s=\"%s\"", pszAttributeName, pszValue);
    1127          13 :                 CPLFree(pszValue);
    1128             :             }
    1129          14 :             CPLFree(pszFieldName);
    1130             :         }
    1131             :     }
    1132             : 
    1133             :     char *pszFieldName =
    1134           8 :         CPLStrdup(CPLSPrintf("%s%s", pszElementName, pszNumber));
    1135           8 :     const int iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
    1136           8 :     if (iIndex != -1 && poFeature->IsFieldSetAndNotNull(iIndex))
    1137             :     {
    1138           6 :         VSIFPrintfL(fp, ">");
    1139             : 
    1140             :         char *pszValue =
    1141           6 :             OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString(iIndex));
    1142           6 :         VSIFPrintfL(fp, "%s", pszValue);
    1143           6 :         CPLFree(pszValue);
    1144             : 
    1145           6 :         VSIFPrintfL(fp, "</%s>\n", pszElementName);
    1146             :     }
    1147             :     else
    1148             :     {
    1149           2 :         VSIFPrintfL(fp, "/>\n");
    1150             :     }
    1151           8 :     CPLFree(pszFieldName);
    1152           8 : }
    1153             : 
    1154             : /************************************************************************/
    1155             : /*                           ICreateFeature()                           */
    1156             : /************************************************************************/
    1157             : 
    1158          95 : OGRErr OGRGeoRSSLayer::ICreateFeature(OGRFeature *poFeatureIn)
    1159             : 
    1160             : {
    1161          95 :     VSILFILE *fp = poDS->GetOutputFP();
    1162          95 :     if (fp == nullptr)
    1163           0 :         return OGRERR_FAILURE;
    1164             : 
    1165          95 :     nNextFID++;
    1166             : 
    1167             :     // Verify that compulsory feeds are set.
    1168             :     // Otherwise put some default value in them.
    1169          95 :     if (eFormat == GEORSS_RSS)
    1170             :     {
    1171          94 :         const int iFieldTitle = poFeatureDefn->GetFieldIndex("title");
    1172             :         const int iFieldDescription =
    1173          94 :             poFeatureDefn->GetFieldIndex("description");
    1174             : 
    1175          94 :         VSIFPrintfL(fp, "    <item>\n");
    1176             : 
    1177         106 :         if ((iFieldTitle == -1 ||
    1178          94 :              !poFeatureIn->IsFieldSetAndNotNull(iFieldTitle)) &&
    1179           0 :             (iFieldDescription == -1 ||
    1180           0 :              !poFeatureIn->IsFieldSetAndNotNull(iFieldDescription)))
    1181             :         {
    1182          82 :             VSIFPrintfL(fp, "      <title>Feature %d</title>\n", nNextFID);
    1183             :         }
    1184             :     }
    1185             :     else
    1186             :     {
    1187           1 :         VSIFPrintfL(fp, "    <entry>\n");
    1188             : 
    1189           1 :         const int iFieldId = poFeatureDefn->GetFieldIndex("id");
    1190           1 :         const int iFieldTitle = poFeatureDefn->GetFieldIndex("title");
    1191           1 :         const int iFieldUpdated = poFeatureDefn->GetFieldIndex("updated");
    1192             : 
    1193           1 :         if (iFieldId == -1 || !poFeatureIn->IsFieldSetAndNotNull(iFieldId))
    1194             :         {
    1195           0 :             VSIFPrintfL(fp, "      <id>Feature %d</id>\n", nNextFID);
    1196             :         }
    1197             : 
    1198           2 :         if (iFieldTitle == -1 ||
    1199           1 :             !poFeatureIn->IsFieldSetAndNotNull(iFieldTitle))
    1200             :         {
    1201           0 :             VSIFPrintfL(fp, "      <title>Title for feature %d</title>\n",
    1202             :                         nNextFID);
    1203             :         }
    1204             : 
    1205           2 :         if (iFieldUpdated == -1 ||
    1206           1 :             !poFeatureIn->IsFieldSetAndNotNull(iFieldUpdated))
    1207             :         {
    1208           0 :             VSIFPrintfL(fp, "      <updated>2009-01-01T00:00:00Z</updated>\n");
    1209             :         }
    1210             :     }
    1211             : 
    1212          95 :     const int nFieldCount = poFeatureDefn->GetFieldCount();
    1213          95 :     int *pbUsed = static_cast<int *>(CPLCalloc(sizeof(int), nFieldCount));
    1214             : 
    1215         226 :     for (int i = 0; i < nFieldCount; i++)
    1216             :     {
    1217         131 :         OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(i);
    1218         131 :         const char *pszName = poFieldDefn->GetNameRef();
    1219             : 
    1220         131 :         if (!poFeatureIn->IsFieldSetAndNotNull(i))
    1221          48 :             continue;
    1222             : 
    1223         166 :         std::string osElementName;
    1224         166 :         std::string osNumber;
    1225         166 :         std::string osAttributeName;
    1226          83 :         OGRGeoRSSLayerSplitComposedField(pszName, osElementName, osNumber,
    1227             :                                          osAttributeName);
    1228             : 
    1229          83 :         bool bWillSkip = false;
    1230             :         // Handle Atom entries with elements with sub-elements like
    1231             :         // <author><name>...</name><uri>...</uri></author>
    1232          83 :         if (eFormat == GEORSS_ATOM)
    1233             :         {
    1234          52 :             for (unsigned int k = 0;
    1235          52 :                  apszAllowedATOMFieldNamesWithSubElements[k] != nullptr; k++)
    1236             :             {
    1237          74 :                 if (osElementName ==
    1238          42 :                         apszAllowedATOMFieldNamesWithSubElements[k] &&
    1239           5 :                     !osAttributeName.empty())
    1240             :                 {
    1241           5 :                     bWillSkip = true;
    1242           5 :                     if (pbUsed[i])
    1243           2 :                         break;
    1244             : 
    1245           3 :                     VSIFPrintfL(fp, "      <%s>\n", osElementName.c_str());
    1246             : 
    1247          23 :                     for (int j = i; j < nFieldCount; j++)
    1248             :                     {
    1249          20 :                         poFieldDefn = poFeatureDefn->GetFieldDefn(j);
    1250          20 :                         if (!poFeatureIn->IsFieldSetAndNotNull(j))
    1251           0 :                             continue;
    1252             : 
    1253          40 :                         std::string osElementName2;
    1254          40 :                         std::string osNumber2;
    1255          40 :                         std::string osAttributeName2;
    1256          20 :                         OGRGeoRSSLayerSplitComposedField(
    1257             :                             poFieldDefn->GetNameRef(), osElementName2,
    1258             :                             osNumber2, osAttributeName2);
    1259             : 
    1260          26 :                         if (osElementName2 == osElementName &&
    1261          26 :                             osNumber2 == osNumber && !osAttributeName2.empty())
    1262             :                         {
    1263           5 :                             pbUsed[j] = TRUE;
    1264             : 
    1265           5 :                             char *pszValue = OGRGetXML_UTF8_EscapedString(
    1266             :                                 poFeatureIn->GetFieldAsString(j));
    1267           5 :                             VSIFPrintfL(fp, "        <%s>%s</%s>\n",
    1268             :                                         osAttributeName2.c_str(), pszValue,
    1269             :                                         osAttributeName2.c_str());
    1270           5 :                             CPLFree(pszValue);
    1271             :                         }
    1272             :                     }
    1273             : 
    1274           3 :                     VSIFPrintfL(fp, "      </%s>\n", osElementName.c_str());
    1275             : 
    1276           3 :                     break;
    1277             :                 }
    1278             :             }
    1279             :         }
    1280             : 
    1281          83 :         if (bWillSkip)
    1282             :         {
    1283             :             // Do nothing
    1284             :         }
    1285          78 :         else if (eFormat == GEORSS_RSS && strcmp(pszName, "pubDate") == 0)
    1286             :         {
    1287          12 :             const OGRField *psField = poFeatureIn->GetRawFieldRef(i);
    1288          12 :             char *pszDate = OGRGetRFC822DateTime(psField);
    1289          12 :             VSIFPrintfL(fp, "      <%s>%s</%s>\n", pszName, pszDate, pszName);
    1290          12 :             CPLFree(pszDate);
    1291             :         }
    1292          66 :         else if (eFormat == GEORSS_ATOM && (strcmp(pszName, "updated") == 0 ||
    1293          14 :                                             strcmp(pszName, "published") == 0))
    1294             :         {
    1295           2 :             const OGRField *psField = poFeatureIn->GetRawFieldRef(i);
    1296           2 :             char *pszDate = OGRGetXMLDateTime(psField);
    1297           2 :             VSIFPrintfL(fp, "      <%s>%s</%s>\n", pszName, pszDate, pszName);
    1298           2 :             CPLFree(pszDate);
    1299             :         }
    1300          64 :         else if (strcmp(pszName, "dc_date") == 0)
    1301             :         {
    1302           0 :             const OGRField *psField = poFeatureIn->GetRawFieldRef(i);
    1303           0 :             char *pszDate = OGRGetXMLDateTime(psField);
    1304           0 :             VSIFPrintfL(fp, "      <%s>%s</%s>\n", "dc:date", pszDate,
    1305             :                         "dc:date");
    1306           0 :             CPLFree(pszDate);
    1307             :         }
    1308             :         // RSS fields with content and attributes.
    1309         154 :         else if (eFormat == GEORSS_RSS &&
    1310         129 :                  (osElementName == "category" || osElementName == "guid" ||
    1311          39 :                   osElementName == "source"))
    1312             :         {
    1313          12 :             if (osAttributeName.empty())
    1314             :             {
    1315           6 :                 OGRGeoRSSLayerWriteSimpleElement(
    1316             :                     fp, osElementName.c_str(), osNumber.c_str(),
    1317             :                     apszAllowedRSSFieldNames, poFeatureDefn, poFeatureIn);
    1318             :             }
    1319             :         }
    1320             :         // RSS field with attribute only.
    1321          52 :         else if (eFormat == GEORSS_RSS && osElementName == "enclosure")
    1322             :         {
    1323           0 :             if (osAttributeName == "url")
    1324             :             {
    1325           0 :                 OGRGeoRSSLayerWriteSimpleElement(
    1326             :                     fp, osElementName.c_str(), osNumber.c_str(),
    1327             :                     apszAllowedRSSFieldNames, poFeatureDefn, poFeatureIn);
    1328             :             }
    1329             :         }
    1330             :         /* ATOM fields with attribute only */
    1331          78 :         else if (eFormat == GEORSS_ATOM &&
    1332          26 :                  (osElementName == "category" || osElementName == "link"))
    1333             :         {
    1334          21 :             if ((osElementName == "category" && osAttributeName == "term") ||
    1335          14 :                 (osElementName == "link" && osAttributeName == "href"))
    1336             :             {
    1337           2 :                 OGRGeoRSSLayerWriteSimpleElement(
    1338             :                     fp, osElementName.c_str(), osNumber.c_str(),
    1339             :                     apszAllowedATOMFieldNames, poFeatureDefn, poFeatureIn);
    1340             :             }
    1341             :         }
    1342          45 :         else if (eFormat == GEORSS_ATOM && (STARTS_WITH(pszName, "content") ||
    1343           2 :                                             STARTS_WITH(pszName, "summary")))
    1344             :         {
    1345           4 :             if (strchr(pszName, '_') == nullptr)
    1346             :             {
    1347           1 :                 VSIFPrintfL(fp, "      <%s", pszName);
    1348             : 
    1349           1 :                 bool bIsXHTML = false;
    1350             :                 char *pszFieldName =
    1351           1 :                     CPLStrdup(CPLSPrintf("%s_%s", pszName, "type"));
    1352           1 :                 int iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
    1353           1 :                 if (iIndex != -1 && poFeatureIn->IsFieldSetAndNotNull(iIndex))
    1354             :                 {
    1355           1 :                     bIsXHTML = strcmp(poFeatureIn->GetFieldAsString(iIndex),
    1356             :                                       "xhtml") == 0;
    1357           1 :                     char *pszValue = OGRGetXML_UTF8_EscapedString(
    1358             :                         poFeatureIn->GetFieldAsString(iIndex));
    1359           1 :                     VSIFPrintfL(fp, " %s=\"%s\"", "type", pszValue);
    1360           1 :                     CPLFree(pszValue);
    1361             :                 }
    1362           1 :                 CPLFree(pszFieldName);
    1363             : 
    1364             :                 pszFieldName =
    1365           1 :                     CPLStrdup(CPLSPrintf("%s_%s", pszName, "xml_lang"));
    1366           1 :                 iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
    1367           1 :                 if (iIndex != -1 && poFeatureIn->IsFieldSetAndNotNull(iIndex))
    1368             :                 {
    1369           1 :                     char *pszValue = OGRGetXML_UTF8_EscapedString(
    1370             :                         poFeatureIn->GetFieldAsString(iIndex));
    1371           1 :                     VSIFPrintfL(fp, " %s=\"%s\"", "xml:lang", pszValue);
    1372           1 :                     CPLFree(pszValue);
    1373             :                 }
    1374           1 :                 CPLFree(pszFieldName);
    1375             : 
    1376             :                 pszFieldName =
    1377           1 :                     CPLStrdup(CPLSPrintf("%s_%s", pszName, "xml_base"));
    1378           1 :                 iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
    1379           1 :                 if (iIndex != -1 && poFeatureIn->IsFieldSetAndNotNull(iIndex))
    1380             :                 {
    1381           1 :                     char *pszValue = OGRGetXML_UTF8_EscapedString(
    1382             :                         poFeatureIn->GetFieldAsString(iIndex));
    1383           1 :                     VSIFPrintfL(fp, " %s=\"%s\"", "xml:base", pszValue);
    1384           1 :                     CPLFree(pszValue);
    1385             :                 }
    1386           1 :                 CPLFree(pszFieldName);
    1387             : 
    1388           1 :                 VSIFPrintfL(fp, ">");
    1389           1 :                 if (bIsXHTML)
    1390             :                 {
    1391           1 :                     VSIFPrintfL(fp, "%s", poFeatureIn->GetFieldAsString(i));
    1392             :                 }
    1393             :                 else
    1394             :                 {
    1395           0 :                     char *pszValue = OGRGetXML_UTF8_EscapedString(
    1396             :                         poFeatureIn->GetFieldAsString(i));
    1397           0 :                     VSIFPrintfL(fp, "%s", pszValue);
    1398           0 :                     CPLFree(pszValue);
    1399             :                 }
    1400           1 :                 VSIFPrintfL(fp, "      </%s>\n", pszName);
    1401           4 :             }
    1402             :         }
    1403          41 :         else if (STARTS_WITH(pszName, "dc_subject"))
    1404             :         {
    1405           0 :             if (strchr(pszName + strlen("dc_subject"), '_') == nullptr)
    1406             :             {
    1407           0 :                 VSIFPrintfL(fp, "      <%s", "dc:subject");
    1408             : 
    1409             :                 char *pszFieldName =
    1410           0 :                     CPLStrdup(CPLSPrintf("%s_%s", pszName, "xml_lang"));
    1411           0 :                 int iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
    1412           0 :                 if (iIndex != -1 && poFeatureIn->IsFieldSetAndNotNull(iIndex))
    1413             :                 {
    1414           0 :                     char *pszValue = OGRGetXML_UTF8_EscapedString(
    1415             :                         poFeatureIn->GetFieldAsString(iIndex));
    1416           0 :                     VSIFPrintfL(fp, " %s=\"%s\"", "xml:lang", pszValue);
    1417           0 :                     CPLFree(pszValue);
    1418             :                 }
    1419           0 :                 CPLFree(pszFieldName);
    1420             : 
    1421           0 :                 char *pszValue = OGRGetXML_UTF8_EscapedString(
    1422             :                     poFeatureIn->GetFieldAsString(i));
    1423           0 :                 VSIFPrintfL(fp, ">%s</%s>\n", pszValue, "dc:subject");
    1424           0 :                 CPLFree(pszValue);
    1425             :             }
    1426             :         }
    1427             :         else
    1428             :         {
    1429          41 :             char *pszTagName = CPLStrdup(pszName);
    1430          41 :             if (IsStandardField(pszName) == FALSE)
    1431             :             {
    1432           3 :                 int nCountUnderscore = 0;
    1433          29 :                 for (int j = 0; pszTagName[j] != 0; j++)
    1434             :                 {
    1435          26 :                     if (pszTagName[j] == '_')
    1436             :                     {
    1437           2 :                         if (nCountUnderscore == 0)
    1438           2 :                             pszTagName[j] = ':';
    1439           2 :                         nCountUnderscore++;
    1440             :                     }
    1441          24 :                     else if (pszTagName[j] == ' ')
    1442           0 :                         pszTagName[j] = '_';
    1443             :                 }
    1444           3 :                 if (nCountUnderscore == 0)
    1445             :                 {
    1446           1 :                     char *pszTemp = CPLStrdup(CPLSPrintf("ogr:%s", pszTagName));
    1447           1 :                     CPLFree(pszTagName);
    1448           1 :                     pszTagName = pszTemp;
    1449             :                 }
    1450             :             }
    1451             :             char *pszValue =
    1452          41 :                 OGRGetXML_UTF8_EscapedString(poFeatureIn->GetFieldAsString(i));
    1453          41 :             VSIFPrintfL(fp, "      <%s>%s</%s>\n", pszTagName, pszValue,
    1454             :                         pszTagName);
    1455          41 :             CPLFree(pszValue);
    1456          41 :             CPLFree(pszTagName);
    1457             :         }
    1458             :     }
    1459             : 
    1460          95 :     CPLFree(pbUsed);
    1461             : 
    1462          95 :     OGRGeoRSSGeomDialect eGeomDialect = poDS->GetGeomDialect();
    1463          95 :     OGRGeometry *poGeom = poFeatureIn->GetGeometryRef();
    1464          95 :     if (poGeom != nullptr && !poGeom->IsEmpty())
    1465             :     {
    1466          59 :         char *pszURN = nullptr;
    1467          59 :         bool bSwapCoordinates = false;
    1468          59 :         if (eGeomDialect == GEORSS_GML)
    1469             :         {
    1470           5 :             if (poSRS != nullptr)
    1471             :             {
    1472           1 :                 const char *pszAuthorityName = poSRS->GetAuthorityName();
    1473           1 :                 const char *pszAuthorityCode = poSRS->GetAuthorityCode();
    1474           1 :                 if (pszAuthorityName != nullptr &&
    1475           1 :                     EQUAL(pszAuthorityName, "EPSG") &&
    1476             :                     pszAuthorityCode != nullptr)
    1477             :                 {
    1478           1 :                     if (!EQUAL(pszAuthorityCode, "4326"))
    1479           1 :                         pszURN = CPLStrdup(CPLSPrintf(
    1480             :                             "urn:ogc:def:crs:EPSG::%s", pszAuthorityCode));
    1481             : 
    1482             :                     /* In case the SRS is a geographic SRS and that we have */
    1483             :                     /* no axis definition, we assume that the order is */
    1484             :                     /* lon/lat. */
    1485             :                     const char *pszAxisName =
    1486           1 :                         poSRS->GetAxis(nullptr, 0, nullptr);
    1487           1 :                     if (poSRS->IsGeographic() &&
    1488           0 :                         (pszAxisName == nullptr ||
    1489           0 :                          STARTS_WITH_CI(pszAxisName, "Lon")))
    1490             :                     {
    1491           0 :                         bSwapCoordinates = true;
    1492           1 :                     }
    1493             :                 }
    1494             :                 else
    1495             :                 {
    1496             :                     static bool bOnce = false;
    1497           0 :                     if (!bOnce)
    1498             :                     {
    1499           0 :                         bOnce = true;
    1500           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    1501             :                                  "Could not translate SRS into GML urn");
    1502             :                     }
    1503             :                 }
    1504             :             }
    1505             :             else
    1506             :             {
    1507           4 :                 bSwapCoordinates = true;
    1508             :             }
    1509             :         }
    1510             : 
    1511          59 :         char szCoord[75] = {};
    1512          59 :         switch (wkbFlatten(poGeom->getGeometryType()))
    1513             :         {
    1514          12 :             case wkbPoint:
    1515             :             {
    1516          12 :                 OGRPoint *poPoint = poGeom->toPoint();
    1517          12 :                 const double x = poPoint->getX();
    1518          12 :                 const double y = poPoint->getY();
    1519          12 :                 if (eGeomDialect == GEORSS_GML)
    1520             :                 {
    1521           2 :                     VSIFPrintfL(fp, "      <georss:where><gml:Point");
    1522           2 :                     if (pszURN != nullptr)
    1523           1 :                         VSIFPrintfL(fp, " srsName=\"%s\"", pszURN);
    1524           2 :                     if (poGeom->getCoordinateDimension() == 3)
    1525             :                     {
    1526           0 :                         OGRMakeWktCoordinate(szCoord, bSwapCoordinates ? y : x,
    1527             :                                              bSwapCoordinates ? x : y,
    1528             :                                              poPoint->getZ(), 3);
    1529           0 :                         VSIFPrintfL(fp, " srsDimension=\"3\"><gml:pos>%s",
    1530             :                                     szCoord);
    1531             :                     }
    1532             :                     else
    1533             :                     {
    1534           2 :                         OGRMakeWktCoordinate(szCoord, bSwapCoordinates ? y : x,
    1535             :                                              bSwapCoordinates ? x : y, 0, 2);
    1536           2 :                         VSIFPrintfL(fp, "><gml:pos>%s", szCoord);
    1537             :                     }
    1538           2 :                     VSIFPrintfL(fp, "</gml:pos></gml:Point></georss:where>\n");
    1539             :                 }
    1540          10 :                 else if (eGeomDialect == GEORSS_SIMPLE)
    1541             :                 {
    1542           9 :                     OGRMakeWktCoordinate(szCoord, y, x, 0, 2);
    1543           9 :                     VSIFPrintfL(fp, "      <georss:point>%s</georss:point>\n",
    1544             :                                 szCoord);
    1545             :                 }
    1546           1 :                 else if (eGeomDialect == GEORSS_W3C_GEO)
    1547             :                 {
    1548           1 :                     OGRFormatDouble(szCoord, sizeof(szCoord), y, '.');
    1549           1 :                     VSIFPrintfL(fp, "      <geo:lat>%s</geo:lat>\n", szCoord);
    1550           1 :                     OGRFormatDouble(szCoord, sizeof(szCoord), x, '.');
    1551           1 :                     VSIFPrintfL(fp, "      <geo:long>%s</geo:long>\n", szCoord);
    1552             :                 }
    1553          12 :                 break;
    1554             :             }
    1555             : 
    1556          11 :             case wkbLineString:
    1557             :             {
    1558          11 :                 OGRLineString *poLineString = poGeom->toLineString();
    1559          11 :                 if (eGeomDialect == GEORSS_GML)
    1560             :                 {
    1561           1 :                     VSIFPrintfL(fp, "      <georss:where><gml:LineString");
    1562           1 :                     if (pszURN != nullptr)
    1563           0 :                         VSIFPrintfL(fp, " srsName=\"%s\"", pszURN);
    1564           1 :                     VSIFPrintfL(fp, "><gml:posList>\n");
    1565           1 :                     const int n = poLineString->getNumPoints();
    1566           4 :                     for (int i = 0; i < n; i++)
    1567             :                     {
    1568           3 :                         const double x = poLineString->getX(i);
    1569           3 :                         const double y = poLineString->getY(i);
    1570           3 :                         OGRMakeWktCoordinate(szCoord, bSwapCoordinates ? y : x,
    1571             :                                              bSwapCoordinates ? x : y, 0, 2);
    1572           3 :                         VSIFPrintfL(fp, "%s ", szCoord);
    1573             :                     }
    1574           1 :                     VSIFPrintfL(
    1575             :                         fp, "</gml:posList></gml:LineString></georss:where>\n");
    1576             :                 }
    1577          10 :                 else if (eGeomDialect == GEORSS_SIMPLE)
    1578             :                 {
    1579           9 :                     VSIFPrintfL(fp, "      <georss:line>\n");
    1580           9 :                     int n = poLineString->getNumPoints();
    1581          28 :                     for (int i = 0; i < n; i++)
    1582             :                     {
    1583          19 :                         double x = poLineString->getX(i);
    1584          19 :                         double y = poLineString->getY(i);
    1585          19 :                         OGRMakeWktCoordinate(szCoord, y, x, 0, 2);
    1586          19 :                         VSIFPrintfL(fp, "%s ", szCoord);
    1587             :                     }
    1588           9 :                     VSIFPrintfL(fp, "</georss:line>\n");
    1589             :                 }
    1590             :                 else
    1591             :                 {
    1592             :                     // Not supported.
    1593             :                 }
    1594          11 :                 break;
    1595             :             }
    1596             : 
    1597          12 :             case wkbPolygon:
    1598             :             {
    1599          12 :                 OGRPolygon *poPolygon = poGeom->toPolygon();
    1600          12 :                 OGRLineString *poLineString = poPolygon->getExteriorRing();
    1601          12 :                 if (poLineString == nullptr)
    1602           0 :                     break;
    1603             : 
    1604          12 :                 if (eGeomDialect == GEORSS_GML)
    1605             :                 {
    1606           2 :                     VSIFPrintfL(fp, "      <georss:where><gml:Polygon");
    1607           2 :                     if (pszURN != nullptr)
    1608           0 :                         VSIFPrintfL(fp, " srsName=\"%s\"", pszURN);
    1609           2 :                     VSIFPrintfL(
    1610             :                         fp, "><gml:exterior><gml:LinearRing><gml:posList>\n");
    1611           2 :                     const int n = poLineString->getNumPoints();
    1612          12 :                     for (int i = 0; i < n; i++)
    1613             :                     {
    1614          10 :                         const double x = poLineString->getX(i);
    1615          10 :                         const double y = poLineString->getY(i);
    1616          10 :                         OGRMakeWktCoordinate(szCoord, bSwapCoordinates ? y : x,
    1617             :                                              bSwapCoordinates ? x : y, 0, 2);
    1618          10 :                         VSIFPrintfL(fp, "%s ", szCoord);
    1619             :                     }
    1620           2 :                     VSIFPrintfL(fp,
    1621             :                                 "</gml:posList></gml:LinearRing></gml:exterior>"
    1622             :                                 "</gml:Polygon></georss:where>\n");
    1623             :                 }
    1624          10 :                 else if (eGeomDialect == GEORSS_SIMPLE)
    1625             :                 {
    1626           8 :                     VSIFPrintfL(fp, "      <georss:polygon>\n");
    1627           8 :                     const int n = poLineString->getNumPoints();
    1628          48 :                     for (int i = 0; i < n; i++)
    1629             :                     {
    1630          40 :                         const double x = poLineString->getX(i);
    1631          40 :                         const double y = poLineString->getY(i);
    1632          40 :                         OGRMakeWktCoordinate(szCoord, y, x, 0, 2);
    1633          40 :                         VSIFPrintfL(fp, "%s ", szCoord);
    1634             :                     }
    1635           8 :                     VSIFPrintfL(fp, "</georss:polygon>\n");
    1636             :                 }
    1637             :                 else
    1638             :                 {
    1639             :                     /* Not supported */
    1640             :                 }
    1641          12 :                 break;
    1642             :             }
    1643             : 
    1644          24 :             default:
    1645             :                 /* Not supported */
    1646          24 :                 break;
    1647             :         }
    1648          59 :         CPLFree(pszURN);
    1649             :     }
    1650             : 
    1651          95 :     if (eFormat == GEORSS_RSS)
    1652          94 :         VSIFPrintfL(fp, "    </item>\n");
    1653             :     else
    1654           1 :         VSIFPrintfL(fp, "    </entry>\n");
    1655             : 
    1656          95 :     return OGRERR_NONE;
    1657             : }
    1658             : 
    1659             : /************************************************************************/
    1660             : /*                            CreateField()                             */
    1661             : /************************************************************************/
    1662             : 
    1663         146 : OGRErr OGRGeoRSSLayer::CreateField(const OGRFieldDefn *poFieldDefn,
    1664             :                                    CPL_UNUSED int bApproxOK)
    1665             : {
    1666         146 :     const char *pszName = poFieldDefn->GetNameRef();
    1667         126 :     if (((eFormat == GEORSS_RSS && strcmp(pszName, "pubDate") == 0) ||
    1668         143 :          (eFormat == GEORSS_ATOM && (strcmp(pszName, "updated") == 0 ||
    1669          19 :                                      strcmp(pszName, "published") == 0)) ||
    1670         292 :          strcmp(pszName, "dc:date") == 0) &&
    1671           5 :         poFieldDefn->GetType() != OFTDateTime)
    1672             :     {
    1673           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s",
    1674             :                  pszName);
    1675           0 :         return OGRERR_FAILURE;
    1676             :     }
    1677             : 
    1678         447 :     for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++)
    1679             :     {
    1680         301 :         if (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(),
    1681         301 :                    pszName) == 0)
    1682             :         {
    1683           0 :             return OGRERR_FAILURE;
    1684             :         }
    1685             :     }
    1686             : 
    1687         146 :     if (IsStandardField(pszName))
    1688             :     {
    1689          47 :         poFeatureDefn->AddFieldDefn(poFieldDefn);
    1690          47 :         return OGRERR_NONE;
    1691             :     }
    1692             : 
    1693          99 :     if (poDS->GetUseExtensions() == FALSE)
    1694             :     {
    1695          96 :         CPLError(
    1696             :             CE_Failure, CPLE_NotSupported,
    1697             :             "Field of name '%s' is not supported in %s schema. "
    1698             :             "Use USE_EXTENSIONS creation option to allow use of extensions.",
    1699          96 :             pszName, (eFormat == GEORSS_RSS) ? "RSS" : "ATOM");
    1700          96 :         return OGRERR_FAILURE;
    1701             :     }
    1702             :     else
    1703             :     {
    1704           3 :         poFeatureDefn->AddFieldDefn(poFieldDefn);
    1705           3 :         return OGRERR_NONE;
    1706             :     }
    1707             : }
    1708             : 
    1709             : #ifdef HAVE_EXPAT
    1710             : 
    1711         304 : static void XMLCALL startElementLoadSchemaCbk(void *pUserData,
    1712             :                                               const char *pszName,
    1713             :                                               const char **ppszAttr)
    1714             : {
    1715         304 :     ((OGRGeoRSSLayer *)pUserData)->startElementLoadSchemaCbk(pszName, ppszAttr);
    1716         304 : }
    1717             : 
    1718         304 : static void XMLCALL endElementLoadSchemaCbk(void *pUserData,
    1719             :                                             const char *pszName)
    1720             : {
    1721         304 :     ((OGRGeoRSSLayer *)pUserData)->endElementLoadSchemaCbk(pszName);
    1722         304 : }
    1723             : 
    1724         889 : static void XMLCALL dataHandlerLoadSchemaCbk(void *pUserData, const char *data,
    1725             :                                              int nLen)
    1726             : {
    1727         889 :     ((OGRGeoRSSLayer *)pUserData)->dataHandlerLoadSchemaCbk(data, nLen);
    1728         889 : }
    1729             : 
    1730             : /************************************************************************/
    1731             : /*                             LoadSchema()                             */
    1732             : /************************************************************************/
    1733             : 
    1734             : /** This function parses the whole file to detect the fields */
    1735          67 : void OGRGeoRSSLayer::LoadSchema()
    1736             : {
    1737          67 :     if (bHasReadSchema)
    1738          54 :         return;
    1739             : 
    1740          67 :     bHasReadSchema = true;
    1741             : 
    1742          67 :     if (fpGeoRSS == nullptr)
    1743          54 :         return;
    1744             : 
    1745          13 :     oSchemaParser = OGRCreateExpatXMLParser();
    1746          13 :     XML_SetElementHandler(oSchemaParser, ::startElementLoadSchemaCbk,
    1747             :                           ::endElementLoadSchemaCbk);
    1748          13 :     XML_SetCharacterDataHandler(oSchemaParser, ::dataHandlerLoadSchemaCbk);
    1749          13 :     XML_SetUserData(oSchemaParser, this);
    1750             : 
    1751          13 :     VSIFSeekL(fpGeoRSS, 0, SEEK_SET);
    1752             : 
    1753          13 :     bInFeature = false;
    1754          13 :     currentDepth = 0;
    1755          13 :     currentFieldDefn = nullptr;
    1756          13 :     pszSubElementName = nullptr;
    1757          13 :     pszSubElementValue = nullptr;
    1758          13 :     nSubElementValueLen = 0;
    1759          13 :     this->bSameSRS = true;
    1760          13 :     CPLFree(this->pszGMLSRSName);
    1761          13 :     this->pszGMLSRSName = nullptr;
    1762          13 :     this->eGeomType = wkbUnknown;
    1763          13 :     this->bFoundGeom = false;
    1764          13 :     bInTagWithSubTag = false;
    1765          13 :     pszTagWithSubTag = nullptr;
    1766          13 :     bStopParsing = false;
    1767          13 :     nWithoutEventCounter = 0;
    1768          13 :     nTotalFeatureCount = 0;
    1769          13 :     setOfFoundFields = nullptr;
    1770             : 
    1771          13 :     std::vector<char> aBuf(PARSER_BUF_SIZE);
    1772          13 :     int nDone = 0;
    1773           0 :     do
    1774             :     {
    1775          13 :         nDataHandlerCounter = 0;
    1776             :         unsigned int nLen =
    1777          13 :             (unsigned int)VSIFReadL(aBuf.data(), 1, aBuf.size(), fpGeoRSS);
    1778          13 :         nDone = nLen < aBuf.size();
    1779          13 :         if (XML_Parse(oSchemaParser, aBuf.data(), nLen, nDone) ==
    1780             :             XML_STATUS_ERROR)
    1781             :         {
    1782           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1783             :                      "XML parsing of GeoRSS file failed: %s",
    1784           0 :                      OGRExpatGetParseError(oSchemaParser));
    1785           0 :             bStopParsing = true;
    1786             :         }
    1787          13 :         nWithoutEventCounter++;
    1788          13 :     } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
    1789             : 
    1790          13 :     XML_ParserFree(oSchemaParser);
    1791             : 
    1792          13 :     if (nWithoutEventCounter == 10)
    1793             :     {
    1794           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1795             :                  "Too much data inside one element. File probably corrupted");
    1796           0 :         bStopParsing = true;
    1797             :     }
    1798             : 
    1799          13 :     CPLAssert(poSRS == nullptr);
    1800          13 :     if (this->bSameSRS && this->bFoundGeom)
    1801             :     {
    1802           9 :         if (this->pszGMLSRSName == nullptr)
    1803             :         {
    1804           8 :             poSRS = new OGRSpatialReference();
    1805           8 :             poSRS->SetWellKnownGeogCS("WGS84");
    1806           8 :             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1807             :         }
    1808             :         else
    1809             :         {
    1810           1 :             poSRS = new OGRSpatialReference();
    1811           1 :             poSRS->importFromURN(pszGMLSRSName);
    1812             :         }
    1813             :     }
    1814             : 
    1815          13 :     if (this->eGeomType != wkbUnknown)
    1816           5 :         poFeatureDefn->SetGeomType(eGeomType);
    1817          13 :     if (poFeatureDefn->GetGeomFieldCount() != 0)
    1818          13 :         poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
    1819             : 
    1820          13 :     if (setOfFoundFields)
    1821          13 :         CPLHashSetDestroy(setOfFoundFields);
    1822          13 :     setOfFoundFields = nullptr;
    1823          13 :     CPLFree(pszGMLSRSName);
    1824          13 :     pszGMLSRSName = nullptr;
    1825          13 :     CPLFree(pszTagWithSubTag);
    1826          13 :     pszTagWithSubTag = nullptr;
    1827             : 
    1828          13 :     VSIFSeekL(fpGeoRSS, 0, SEEK_SET);
    1829             : }
    1830             : 
    1831             : /************************************************************************/
    1832             : /*                     startElementLoadSchemaCbk()                      */
    1833             : /************************************************************************/
    1834             : 
    1835         304 : void OGRGeoRSSLayer::startElementLoadSchemaCbk(const char *pszName,
    1836             :                                                const char **ppszAttr)
    1837             : {
    1838         304 :     if (bStopParsing)
    1839           0 :         return;
    1840             : 
    1841         304 :     nWithoutEventCounter = 0;
    1842         304 :     const char *pszNoNSName = pszName;
    1843         304 :     const char *pszColon = strchr(pszNoNSName, ':');
    1844         304 :     if (pszColon)
    1845          77 :         pszNoNSName = pszColon + 1;
    1846             : 
    1847         304 :     if ((eFormat == GEORSS_ATOM && currentDepth == 1 &&
    1848          23 :          strcmp(pszNoNSName, "entry") == 0) ||
    1849         301 :         ((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) && !bInFeature &&
    1850          68 :          (currentDepth == 1 || currentDepth == 2) &&
    1851          58 :          strcmp(pszNoNSName, "item") == 0))
    1852             :     {
    1853          28 :         bInFeature = true;
    1854          28 :         featureDepth = currentDepth;
    1855             : 
    1856          28 :         nTotalFeatureCount++;
    1857             : 
    1858          28 :         if (setOfFoundFields)
    1859          15 :             CPLHashSetDestroy(setOfFoundFields);
    1860          28 :         setOfFoundFields =
    1861          28 :             CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, CPLFree);
    1862             :     }
    1863         276 :     else if (bInTagWithSubTag && currentDepth == 3)
    1864             :     {
    1865             :         char *pszFieldName =
    1866          15 :             CPLStrdup(CPLSPrintf("%s_%s", pszTagWithSubTag, pszNoNSName));
    1867          15 :         if (poFeatureDefn->GetFieldIndex(pszFieldName) == -1)
    1868             :         {
    1869          30 :             OGRFieldDefn newFieldDefn(pszFieldName, OFTString);
    1870          15 :             poFeatureDefn->AddFieldDefn(&newFieldDefn);
    1871             : 
    1872          15 :             if (poFeatureDefn->GetFieldCount() == 100)
    1873             :             {
    1874           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1875             :                          "Too many fields. File probably corrupted");
    1876           0 :                 XML_StopParser(oSchemaParser, XML_FALSE);
    1877           0 :                 bStopParsing = true;
    1878             :             }
    1879             :         }
    1880          15 :         CPLFree(pszFieldName);
    1881             :     }
    1882         291 :     else if (bInFeature && eFormat == GEORSS_ATOM && currentDepth == 2 &&
    1883          30 :              OGRGeoRSSLayerATOMTagHasSubElement(pszNoNSName))
    1884             :     {
    1885           9 :         CPLFree(pszTagWithSubTag);
    1886           9 :         pszTagWithSubTag = CPLStrdup(pszNoNSName);
    1887             : 
    1888           9 :         int count = 1;
    1889          12 :         while (CPLHashSetLookup(setOfFoundFields, pszTagWithSubTag) != nullptr)
    1890             :         {
    1891           3 :             count++;
    1892           3 :             CPLFree(pszTagWithSubTag);
    1893           3 :             pszTagWithSubTag =
    1894           3 :                 CPLStrdup(CPLSPrintf("%s%d", pszNoNSName, count));
    1895           3 :             if (pszTagWithSubTag[0] == 0)
    1896             :             {
    1897           0 :                 XML_StopParser(oSchemaParser, XML_FALSE);
    1898           0 :                 bStopParsing = true;
    1899           0 :                 break;
    1900             :             }
    1901             :         }
    1902           9 :         CPLHashSetInsert(setOfFoundFields, CPLStrdup(pszTagWithSubTag));
    1903             : 
    1904           9 :         bInTagWithSubTag = true;
    1905             :     }
    1906         252 :     else if (bInFeature && currentDepth == featureDepth + 1 &&
    1907         149 :              !IS_GEO_ELEMENT(pszName))
    1908             :     {
    1909         127 :         if (pszName != pszNoNSName && STARTS_WITH(pszName, "atom:"))
    1910           7 :             pszName = pszNoNSName;
    1911             : 
    1912         127 :         CPLFree(pszSubElementName);
    1913         127 :         pszSubElementName = CPLStrdup(pszName);
    1914             : 
    1915         127 :         int count = 1;
    1916         135 :         while (CPLHashSetLookup(setOfFoundFields, pszSubElementName) != nullptr)
    1917             :         {
    1918           8 :             count++;
    1919           8 :             CPLFree(pszSubElementName);
    1920           8 :             pszSubElementName = CPLStrdup(CPLSPrintf("%s%d", pszName, count));
    1921             :         }
    1922         127 :         CPLHashSetInsert(setOfFoundFields, CPLStrdup(pszSubElementName));
    1923             : 
    1924             :         // Create field definition for element.
    1925             :         char *pszCompatibleName =
    1926         127 :             OGRGeoRSS_GetOGRCompatibleTagName(pszSubElementName);
    1927         127 :         int iField = poFeatureDefn->GetFieldIndex(pszCompatibleName);
    1928         127 :         if (iField >= 0)
    1929             :         {
    1930          66 :             currentFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1931             :         }
    1932          61 :         else if (!((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) &&
    1933          40 :                    strcmp(pszNoNSName, "enclosure") == 0) &&
    1934          61 :                  !(eFormat == GEORSS_ATOM &&
    1935          21 :                    strcmp(pszNoNSName, "link") == 0) &&
    1936          55 :                  !(eFormat == GEORSS_ATOM &&
    1937          15 :                    strcmp(pszNoNSName, "category") == 0))
    1938             :         {
    1939             :             OGRFieldType eFieldType;
    1940          55 :             if (((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) &&
    1941          40 :                  strcmp(pszNoNSName, "pubDate") == 0) ||
    1942          49 :                 (eFormat == GEORSS_ATOM &&
    1943          15 :                  strcmp(pszNoNSName, "updated") == 0) ||
    1944          46 :                 (eFormat == GEORSS_ATOM &&
    1945          12 :                  strcmp(pszNoNSName, "published") == 0) ||
    1946          43 :                 strcmp(pszName, "dc:date") == 0)
    1947          12 :                 eFieldType = OFTDateTime;
    1948             :             else
    1949          43 :                 eFieldType = OFTInteger;
    1950             : 
    1951         110 :             OGRFieldDefn newFieldDefn(pszCompatibleName, eFieldType);
    1952          55 :             poFeatureDefn->AddFieldDefn(&newFieldDefn);
    1953          55 :             currentFieldDefn =
    1954          55 :                 poFeatureDefn->GetFieldDefn(poFeatureDefn->GetFieldCount() - 1);
    1955             : 
    1956          55 :             if (poFeatureDefn->GetFieldCount() == 100)
    1957             :             {
    1958           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1959             :                          "Too many fields. File probably corrupted");
    1960           0 :                 XML_StopParser(oSchemaParser, XML_FALSE);
    1961           0 :                 bStopParsing = true;
    1962             :             }
    1963             :         }
    1964             : 
    1965             :         // Create field definitions for attributes.
    1966         168 :         for (int i = 0; ppszAttr[i] != nullptr && ppszAttr[i + 1] != nullptr &&
    1967          41 :                         !bStopParsing;
    1968          41 :              i += 2)
    1969             :         {
    1970          41 :             char *pszAttrCompatibleName = OGRGeoRSS_GetOGRCompatibleTagName(
    1971          41 :                 CPLSPrintf("%s_%s", pszSubElementName, ppszAttr[i]));
    1972          41 :             iField = poFeatureDefn->GetFieldIndex(pszAttrCompatibleName);
    1973          41 :             OGRFieldDefn *currentAttrFieldDefn = nullptr;
    1974          41 :             if (iField >= 0)
    1975             :             {
    1976           0 :                 currentAttrFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1977             :             }
    1978             :             else
    1979             :             {
    1980          82 :                 OGRFieldDefn newFieldDefn(pszAttrCompatibleName, OFTInteger);
    1981          41 :                 poFeatureDefn->AddFieldDefn(&newFieldDefn);
    1982         123 :                 currentAttrFieldDefn = poFeatureDefn->GetFieldDefn(
    1983          41 :                     poFeatureDefn->GetFieldCount() - 1);
    1984             : 
    1985          41 :                 if (poFeatureDefn->GetFieldCount() == 100)
    1986             :                 {
    1987           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1988             :                              "Too many fields. File probably corrupted");
    1989           0 :                     XML_StopParser(oSchemaParser, XML_FALSE);
    1990           0 :                     bStopParsing = true;
    1991             :                 }
    1992             :             }
    1993          41 :             if (currentAttrFieldDefn->GetType() == OFTInteger ||
    1994           0 :                 currentAttrFieldDefn->GetType() == OFTReal)
    1995             :             {
    1996          41 :                 const CPLValueType eType = CPLGetValueType(ppszAttr[i + 1]);
    1997          41 :                 if (eType == CPL_VALUE_REAL)
    1998             :                 {
    1999           0 :                     currentAttrFieldDefn->SetType(OFTReal);
    2000             :                 }
    2001          41 :                 else if (eType == CPL_VALUE_STRING)
    2002             :                 {
    2003          38 :                     currentAttrFieldDefn->SetType(OFTString);
    2004             :                 }
    2005             :             }
    2006          41 :             CPLFree(pszAttrCompatibleName);
    2007             :         }
    2008             : 
    2009         127 :         CPLFree(pszCompatibleName);
    2010             :     }
    2011         125 :     else if (strcmp(pszName, "georss:point") == 0 ||
    2012         122 :              strcmp(pszName, "georss:line") == 0 ||
    2013         120 :              strcmp(pszName, "geo:line") == 0 || IS_LAT_ELEMENT(pszName) ||
    2014         119 :              strcmp(pszName, "georss:polygon") == 0 ||
    2015         116 :              strcmp(pszName, "georss:box") == 0)
    2016             :     {
    2017          11 :         if (bSameSRS)
    2018             :         {
    2019          11 :             if (pszGMLSRSName != nullptr)
    2020           0 :                 bSameSRS = false;
    2021             :         }
    2022             :     }
    2023         114 :     else if (strcmp(pszName, "gml:Point") == 0 ||
    2024         111 :              strcmp(pszName, "gml:LineString") == 0 ||
    2025         108 :              strcmp(pszName, "gml:Polygon") == 0 ||
    2026         105 :              strcmp(pszName, "gml:MultiPoint") == 0 ||
    2027         105 :              strcmp(pszName, "gml:MultiLineString") == 0 ||
    2028         105 :              strcmp(pszName, "gml:MultiPolygon") == 0 ||
    2029         105 :              strcmp(pszName, "gml:Envelope") == 0)
    2030             :     {
    2031          10 :         if (bSameSRS)
    2032             :         {
    2033          10 :             bool bFoundSRS = false;
    2034          10 :             for (int i = 0; ppszAttr[i] != nullptr; i += 2)
    2035             :             {
    2036           1 :                 if (strcmp(ppszAttr[i], "srsName") == 0)
    2037             :                 {
    2038           1 :                     bFoundSRS = true;
    2039           1 :                     if (pszGMLSRSName != nullptr)
    2040             :                     {
    2041           0 :                         if (strcmp(pszGMLSRSName, ppszAttr[i + 1]) != 0)
    2042           0 :                             bSameSRS = false;
    2043             :                     }
    2044             :                     else
    2045           1 :                         pszGMLSRSName = CPLStrdup(ppszAttr[i + 1]);
    2046           1 :                     break;
    2047             :                 }
    2048             :             }
    2049          10 :             if (!bFoundSRS && pszGMLSRSName != nullptr)
    2050           0 :                 bSameSRS = false;
    2051             :         }
    2052             :     }
    2053             : 
    2054         304 :     if (!bInFeature || currentDepth >= featureDepth + 1)
    2055             :     {
    2056         276 :         int nDimension = 2;
    2057         367 :         for (int i = 0; ppszAttr[i] != nullptr && ppszAttr[i + 1] != nullptr;
    2058          91 :              i += 2)
    2059             :         {
    2060          91 :             if (strcmp(ppszAttr[i], "srsDimension") == 0)
    2061             :             {
    2062           0 :                 nDimension = atoi(ppszAttr[i + 1]);
    2063           0 :                 break;
    2064             :             }
    2065             :         }
    2066             : 
    2067         276 :         OGRwkbGeometryType eFoundGeomType = wkbUnknown;
    2068         276 :         if (strcmp(pszName, "georss:point") == 0 || IS_LAT_ELEMENT(pszName) ||
    2069         272 :             strcmp(pszName, "gml:Point") == 0)
    2070             :         {
    2071           7 :             eFoundGeomType = wkbPoint;
    2072             :         }
    2073         269 :         else if (strcmp(pszName, "gml:MultiPoint") == 0)
    2074             :         {
    2075           0 :             eFoundGeomType = wkbMultiPoint;
    2076             :         }
    2077         269 :         else if (strcmp(pszName, "georss:line") == 0 ||
    2078         267 :                  strcmp(pszName, "geo:line") == 0 ||
    2079         267 :                  strcmp(pszName, "gml:LineString") == 0)
    2080             :         {
    2081           5 :             eFoundGeomType = wkbLineString;
    2082             :         }
    2083         264 :         else if (strcmp(pszName, "gml:MultiLineString") == 0)
    2084             :         {
    2085           0 :             eFoundGeomType = wkbMultiLineString;
    2086             :         }
    2087         264 :         else if (strcmp(pszName, "georss:polygon") == 0 ||
    2088         261 :                  strcmp(pszName, "gml:Polygon") == 0 ||
    2089         258 :                  strcmp(pszName, "gml:Envelope") == 0 ||
    2090         257 :                  strcmp(pszName, "georss:box") == 0)
    2091             :         {
    2092           9 :             eFoundGeomType = wkbPolygon;
    2093             :         }
    2094         255 :         else if (strcmp(pszName, "gml:MultiPolygon") == 0)
    2095             :         {
    2096           0 :             eFoundGeomType = wkbMultiPolygon;
    2097             :         }
    2098             : 
    2099         276 :         if (eFoundGeomType != wkbUnknown)
    2100             :         {
    2101          21 :             if (!bFoundGeom)
    2102             :             {
    2103           9 :                 eGeomType = eFoundGeomType;
    2104           9 :                 bFoundGeom = true;
    2105             :             }
    2106          12 :             else if (wkbFlatten(eGeomType) != eFoundGeomType)
    2107             :             {
    2108          12 :                 eGeomType = wkbUnknown;
    2109             :             }
    2110             : 
    2111          21 :             if (nDimension == 3)
    2112           0 :                 eGeomType = wkbSetZ(eGeomType);
    2113             :         }
    2114             :     }
    2115             : 
    2116         304 :     currentDepth++;
    2117             : }
    2118             : 
    2119             : /************************************************************************/
    2120             : /*                      endElementLoadSchemaCbk()                       */
    2121             : /************************************************************************/
    2122             : 
    2123         304 : void OGRGeoRSSLayer::endElementLoadSchemaCbk(const char *pszName)
    2124             : {
    2125         304 :     if (bStopParsing)
    2126           0 :         return;
    2127             : 
    2128         304 :     nWithoutEventCounter = 0;
    2129             : 
    2130         304 :     currentDepth--;
    2131             : 
    2132         304 :     if (!bInFeature)
    2133          67 :         return;
    2134             : 
    2135         237 :     const char *pszNoNSName = pszName;
    2136         237 :     const char *pszColon = strchr(pszNoNSName, ':');
    2137         237 :     if (pszColon)
    2138          68 :         pszNoNSName = pszColon + 1;
    2139             : 
    2140         237 :     if ((eFormat == GEORSS_ATOM && currentDepth == 1 &&
    2141           3 :          strcmp(pszNoNSName, "entry") == 0) ||
    2142         234 :         ((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) &&
    2143         180 :          (currentDepth == 1 || currentDepth == 2) &&
    2144          25 :          strcmp(pszNoNSName, "item") == 0))
    2145             :     {
    2146          28 :         bInFeature = false;
    2147             :     }
    2148         239 :     else if (eFormat == GEORSS_ATOM && currentDepth == 2 &&
    2149          30 :              OGRGeoRSSLayerATOMTagHasSubElement(pszNoNSName))
    2150             :     {
    2151           9 :         bInTagWithSubTag = false;
    2152             :     }
    2153         200 :     else if (currentDepth == featureDepth + 1 && pszSubElementName)
    2154             :     {
    2155             :         // Patch field type.
    2156         127 :         if (pszSubElementValue && nSubElementValueLen && currentFieldDefn)
    2157             :         {
    2158         121 :             pszSubElementValue[nSubElementValueLen] = 0;
    2159         199 :             if (currentFieldDefn->GetType() == OFTInteger ||
    2160          78 :                 currentFieldDefn->GetType() == OFTReal)
    2161             :             {
    2162          43 :                 const CPLValueType eType = CPLGetValueType(pszSubElementValue);
    2163          43 :                 if (eType == CPL_VALUE_REAL)
    2164             :                 {
    2165           0 :                     currentFieldDefn->SetType(OFTReal);
    2166             :                 }
    2167          43 :                 else if (eType == CPL_VALUE_STRING)
    2168             :                 {
    2169          42 :                     currentFieldDefn->SetType(OFTString);
    2170             :                 }
    2171             :             }
    2172             :         }
    2173             : 
    2174         127 :         CPLFree(pszSubElementName);
    2175         127 :         pszSubElementName = nullptr;
    2176         127 :         CPLFree(pszSubElementValue);
    2177         127 :         pszSubElementValue = nullptr;
    2178         127 :         nSubElementValueLen = 0;
    2179         127 :         currentFieldDefn = nullptr;
    2180             :     }
    2181             : }
    2182             : 
    2183             : /************************************************************************/
    2184             : /*                      dataHandlerLoadSchemaCbk()                      */
    2185             : /************************************************************************/
    2186             : 
    2187         889 : void OGRGeoRSSLayer::dataHandlerLoadSchemaCbk(const char *data, int nLen)
    2188             : {
    2189         889 :     if (bStopParsing)
    2190           0 :         return;
    2191             : 
    2192         889 :     nDataHandlerCounter++;
    2193         889 :     if (nDataHandlerCounter >= PARSER_BUF_SIZE)
    2194             :     {
    2195           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2196             :                  "File probably corrupted (million laugh pattern)");
    2197           0 :         XML_StopParser(oSchemaParser, XML_FALSE);
    2198           0 :         bStopParsing = true;
    2199           0 :         return;
    2200             :     }
    2201             : 
    2202         889 :     nWithoutEventCounter = 0;
    2203             : 
    2204         889 :     if (pszSubElementName)
    2205             :     {
    2206         138 :         char *pszNewSubElementValue = static_cast<char *>(VSI_REALLOC_VERBOSE(
    2207             :             pszSubElementValue, nSubElementValueLen + nLen + 1));
    2208         138 :         if (pszNewSubElementValue == nullptr)
    2209             :         {
    2210           0 :             XML_StopParser(oSchemaParser, XML_FALSE);
    2211           0 :             bStopParsing = true;
    2212           0 :             return;
    2213             :         }
    2214         138 :         pszSubElementValue = pszNewSubElementValue;
    2215         138 :         memcpy(pszSubElementValue + nSubElementValueLen, data, nLen);
    2216         138 :         nSubElementValueLen += nLen;
    2217         138 :         if (nSubElementValueLen > 100000)
    2218             :         {
    2219           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2220             :                      "Too much data inside one element. "
    2221             :                      "File probably corrupted");
    2222           0 :             XML_StopParser(oSchemaParser, XML_FALSE);
    2223           0 :             bStopParsing = true;
    2224             :         }
    2225             :     }
    2226             : }
    2227             : #else
    2228             : void OGRGeoRSSLayer::LoadSchema()
    2229             : {
    2230             : }
    2231             : #endif
    2232             : 
    2233             : /************************************************************************/
    2234             : /*                           TestCapability()                           */
    2235             : /************************************************************************/
    2236             : 
    2237         108 : bool OGRGeoRSSLayer::TestCapability(const char *pszCap) const
    2238             : 
    2239             : {
    2240         108 :     if (EQUAL(pszCap, OLCFastFeatureCount))
    2241           0 :         return !bWriteMode && bHasReadSchema && m_poFilterGeom == nullptr &&
    2242           0 :                m_poAttrQuery == nullptr;
    2243             : 
    2244         108 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
    2245           0 :         return TRUE;
    2246             : 
    2247         108 :     else if (EQUAL(pszCap, OLCSequentialWrite))
    2248          16 :         return bWriteMode;
    2249          92 :     else if (EQUAL(pszCap, OLCCreateField))
    2250          16 :         return bWriteMode;
    2251          76 :     else if (EQUAL(pszCap, OLCZGeometries))
    2252           0 :         return TRUE;
    2253             :     else
    2254          76 :         return FALSE;
    2255             : }
    2256             : 
    2257             : /************************************************************************/
    2258             : /*                          GetFeatureCount()                           */
    2259             : /************************************************************************/
    2260             : 
    2261           0 : GIntBig OGRGeoRSSLayer::GetFeatureCount(int bForce)
    2262             : 
    2263             : {
    2264           0 :     if (bWriteMode)
    2265             :     {
    2266           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2267             :                  "Cannot read features when writing a GeoRSS file");
    2268           0 :         return 0;
    2269             :     }
    2270             : 
    2271           0 :     if (!bHasReadSchema)
    2272           0 :         LoadSchema();
    2273             : 
    2274           0 :     if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
    2275           0 :         return OGRLayer::GetFeatureCount(bForce);
    2276             : 
    2277           0 :     return nTotalFeatureCount;
    2278             : }
    2279             : 
    2280             : /************************************************************************/
    2281             : /*                             GetDataset()                             */
    2282             : /************************************************************************/
    2283             : 
    2284          19 : GDALDataset *OGRGeoRSSLayer::GetDataset()
    2285             : {
    2286          19 :     return poDS;
    2287             : }

Generated by: LCOV version 1.14