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 1192 87.3 %
Date: 2026-02-12 23:49:34 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             :                      "at line %d, column %d",
     976             :                      XML_ErrorString(XML_GetErrorCode(oParser)),
     977           0 :                      static_cast<int>(XML_GetCurrentLineNumber(oParser)),
     978           0 :                      static_cast<int>(XML_GetCurrentColumnNumber(oParser)));
     979           0 :             bStopParsing = true;
     980             :         }
     981          13 :     } while (!nDone && !bStopParsing && this->nFeatureTabLength == 0);
     982             : 
     983          13 :     return this->nFeatureTabLength ? ppoFeatureTab[nFeatureTabIndex++]
     984          13 :                                    : nullptr;
     985             : #else
     986             :     return nullptr;
     987             : #endif
     988             : }
     989             : 
     990             : /************************************************************************/
     991             : /*               OGRGeoRSSLayerIsStandardFieldInternal()                */
     992             : /************************************************************************/
     993             : 
     994         187 : static bool OGRGeoRSSLayerIsStandardFieldInternal(const char *pszName,
     995             :                                                   const char *const *papszNames)
     996             : {
     997        2223 :     for (unsigned int i = 0; papszNames[i] != nullptr; i++)
     998             :     {
     999        2121 :         if (strcmp(pszName, papszNames[i]) == 0)
    1000             :         {
    1001          74 :             return true;
    1002             :         }
    1003             : 
    1004        2047 :         const char *pszUnderscore = strchr(papszNames[i], '_');
    1005        2047 :         if (pszUnderscore == nullptr)
    1006             :         {
    1007        1115 :             size_t nLen = strlen(papszNames[i]);
    1008        1115 :             if (strncmp(pszName, papszNames[i], nLen) == 0)
    1009             :             {
    1010          12 :                 size_t k = nLen;
    1011          18 :                 while (pszName[k] >= '0' && pszName[k] <= '9')
    1012           6 :                     k++;
    1013          12 :                 if (pszName[k] == '\0')
    1014           3 :                     return true;
    1015             :             }
    1016             :         }
    1017             :         else
    1018             :         {
    1019         932 :             const size_t nLen =
    1020         932 :                 static_cast<size_t>(pszUnderscore - papszNames[i]);
    1021         932 :             if (strncmp(pszName, papszNames[i], nLen) == 0)
    1022             :             {
    1023          23 :                 size_t k = nLen;
    1024          37 :                 while (pszName[k] >= '0' && pszName[k] <= '9')
    1025          14 :                     k++;
    1026          23 :                 if (pszName[k] == '_' &&
    1027          23 :                     strcmp(pszName + k, pszUnderscore) == 0)
    1028           8 :                     return true;
    1029             :             }
    1030             :         }
    1031             :     }
    1032         102 :     return false;
    1033             : }
    1034             : 
    1035             : /************************************************************************/
    1036             : /*                  OGRGeoRSSLayer::IsStandardField()                   */
    1037             : /************************************************************************/
    1038             : 
    1039         187 : bool OGRGeoRSSLayer::IsStandardField(const char *pszName)
    1040             : {
    1041         187 :     if (eFormat == GEORSS_RSS)
    1042             :     {
    1043         165 :         return OGRGeoRSSLayerIsStandardFieldInternal(pszName,
    1044         165 :                                                      apszAllowedRSSFieldNames);
    1045             :     }
    1046             :     else
    1047             :     {
    1048          22 :         return OGRGeoRSSLayerIsStandardFieldInternal(pszName,
    1049          22 :                                                      apszAllowedATOMFieldNames);
    1050             :     }
    1051             : }
    1052             : 
    1053             : /************************************************************************/
    1054             : /*                  OGRGeoRSSLayerSplitComposedField()                  */
    1055             : /************************************************************************/
    1056             : 
    1057         103 : static void OGRGeoRSSLayerSplitComposedField(const char *pszName,
    1058             :                                              std::string &osElementName,
    1059             :                                              std::string &osNumber,
    1060             :                                              std::string &osAttributeName)
    1061             : {
    1062         103 :     osElementName = pszName;
    1063             : 
    1064         103 :     int i = 0;
    1065         751 :     while (pszName[i] != '\0' && pszName[i] != '_' &&
    1066         663 :            !(pszName[i] >= '0' && pszName[i] <= '9'))
    1067             :     {
    1068         648 :         i++;
    1069             :     }
    1070             : 
    1071         103 :     osElementName.resize(i);
    1072             : 
    1073         103 :     if (pszName[i] >= '0' && pszName[i] <= '9')
    1074             :     {
    1075          15 :         osNumber = pszName + i;
    1076          15 :         const auto nPos = osNumber.find('_');
    1077          15 :         if (nPos != std::string::npos)
    1078             :         {
    1079          11 :             osAttributeName = osNumber.substr(nPos + 1);
    1080          11 :             osNumber.resize(nPos);
    1081             :         }
    1082             :         else
    1083             :         {
    1084           4 :             osAttributeName.clear();
    1085          15 :         }
    1086             :     }
    1087             :     else
    1088             :     {
    1089          88 :         osNumber.clear();
    1090          88 :         if (pszName[i] == '_')
    1091             :         {
    1092          29 :             osAttributeName = pszName + i + 1;
    1093             :         }
    1094             :         else
    1095             :         {
    1096          59 :             osAttributeName.clear();
    1097             :         }
    1098             :     }
    1099         103 : }
    1100             : 
    1101             : /************************************************************************/
    1102             : /*                  OGRGeoRSSLayerWriteSimpleElement()                  */
    1103             : /************************************************************************/
    1104             : 
    1105           8 : static void OGRGeoRSSLayerWriteSimpleElement(VSILFILE *fp,
    1106             :                                              const char *pszElementName,
    1107             :                                              const char *pszNumber,
    1108             :                                              const char *const *papszNames,
    1109             :                                              OGRFeatureDefn *poFeatureDefn,
    1110             :                                              OGRFeature *poFeature)
    1111             : {
    1112           8 :     VSIFPrintfL(fp, "      <%s", pszElementName);
    1113             : 
    1114         152 :     for (unsigned int k = 0; papszNames[k] != nullptr; k++)
    1115             :     {
    1116         144 :         if (strncmp(papszNames[k], pszElementName, strlen(pszElementName)) ==
    1117          20 :                 0 &&
    1118          20 :             papszNames[k][strlen(pszElementName)] == '_')
    1119             :         {
    1120          14 :             const char *pszAttributeName =
    1121          14 :                 papszNames[k] + strlen(pszElementName) + 1;
    1122          14 :             char *pszFieldName = CPLStrdup(CPLSPrintf(
    1123             :                 "%s%s_%s", pszElementName, pszNumber, pszAttributeName));
    1124          14 :             int iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
    1125          14 :             if (iIndex != -1 && poFeature->IsFieldSetAndNotNull(iIndex))
    1126             :             {
    1127          13 :                 char *pszValue = OGRGetXML_UTF8_EscapedString(
    1128             :                     poFeature->GetFieldAsString(iIndex));
    1129          13 :                 VSIFPrintfL(fp, " %s=\"%s\"", pszAttributeName, pszValue);
    1130          13 :                 CPLFree(pszValue);
    1131             :             }
    1132          14 :             CPLFree(pszFieldName);
    1133             :         }
    1134             :     }
    1135             : 
    1136             :     char *pszFieldName =
    1137           8 :         CPLStrdup(CPLSPrintf("%s%s", pszElementName, pszNumber));
    1138           8 :     const int iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
    1139           8 :     if (iIndex != -1 && poFeature->IsFieldSetAndNotNull(iIndex))
    1140             :     {
    1141           6 :         VSIFPrintfL(fp, ">");
    1142             : 
    1143             :         char *pszValue =
    1144           6 :             OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString(iIndex));
    1145           6 :         VSIFPrintfL(fp, "%s", pszValue);
    1146           6 :         CPLFree(pszValue);
    1147             : 
    1148           6 :         VSIFPrintfL(fp, "</%s>\n", pszElementName);
    1149             :     }
    1150             :     else
    1151             :     {
    1152           2 :         VSIFPrintfL(fp, "/>\n");
    1153             :     }
    1154           8 :     CPLFree(pszFieldName);
    1155           8 : }
    1156             : 
    1157             : /************************************************************************/
    1158             : /*                           ICreateFeature()                           */
    1159             : /************************************************************************/
    1160             : 
    1161          95 : OGRErr OGRGeoRSSLayer::ICreateFeature(OGRFeature *poFeatureIn)
    1162             : 
    1163             : {
    1164          95 :     VSILFILE *fp = poDS->GetOutputFP();
    1165          95 :     if (fp == nullptr)
    1166           0 :         return OGRERR_FAILURE;
    1167             : 
    1168          95 :     nNextFID++;
    1169             : 
    1170             :     // Verify that compulsory feeds are set.
    1171             :     // Otherwise put some default value in them.
    1172          95 :     if (eFormat == GEORSS_RSS)
    1173             :     {
    1174          94 :         const int iFieldTitle = poFeatureDefn->GetFieldIndex("title");
    1175             :         const int iFieldDescription =
    1176          94 :             poFeatureDefn->GetFieldIndex("description");
    1177             : 
    1178          94 :         VSIFPrintfL(fp, "    <item>\n");
    1179             : 
    1180         106 :         if ((iFieldTitle == -1 ||
    1181          94 :              !poFeatureIn->IsFieldSetAndNotNull(iFieldTitle)) &&
    1182           0 :             (iFieldDescription == -1 ||
    1183           0 :              !poFeatureIn->IsFieldSetAndNotNull(iFieldDescription)))
    1184             :         {
    1185          82 :             VSIFPrintfL(fp, "      <title>Feature %d</title>\n", nNextFID);
    1186             :         }
    1187             :     }
    1188             :     else
    1189             :     {
    1190           1 :         VSIFPrintfL(fp, "    <entry>\n");
    1191             : 
    1192           1 :         const int iFieldId = poFeatureDefn->GetFieldIndex("id");
    1193           1 :         const int iFieldTitle = poFeatureDefn->GetFieldIndex("title");
    1194           1 :         const int iFieldUpdated = poFeatureDefn->GetFieldIndex("updated");
    1195             : 
    1196           1 :         if (iFieldId == -1 || !poFeatureIn->IsFieldSetAndNotNull(iFieldId))
    1197             :         {
    1198           0 :             VSIFPrintfL(fp, "      <id>Feature %d</id>\n", nNextFID);
    1199             :         }
    1200             : 
    1201           2 :         if (iFieldTitle == -1 ||
    1202           1 :             !poFeatureIn->IsFieldSetAndNotNull(iFieldTitle))
    1203             :         {
    1204           0 :             VSIFPrintfL(fp, "      <title>Title for feature %d</title>\n",
    1205             :                         nNextFID);
    1206             :         }
    1207             : 
    1208           2 :         if (iFieldUpdated == -1 ||
    1209           1 :             !poFeatureIn->IsFieldSetAndNotNull(iFieldUpdated))
    1210             :         {
    1211           0 :             VSIFPrintfL(fp, "      <updated>2009-01-01T00:00:00Z</updated>\n");
    1212             :         }
    1213             :     }
    1214             : 
    1215          95 :     const int nFieldCount = poFeatureDefn->GetFieldCount();
    1216          95 :     int *pbUsed = static_cast<int *>(CPLCalloc(sizeof(int), nFieldCount));
    1217             : 
    1218         226 :     for (int i = 0; i < nFieldCount; i++)
    1219             :     {
    1220         131 :         OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(i);
    1221         131 :         const char *pszName = poFieldDefn->GetNameRef();
    1222             : 
    1223         131 :         if (!poFeatureIn->IsFieldSetAndNotNull(i))
    1224          48 :             continue;
    1225             : 
    1226         166 :         std::string osElementName;
    1227         166 :         std::string osNumber;
    1228         166 :         std::string osAttributeName;
    1229          83 :         OGRGeoRSSLayerSplitComposedField(pszName, osElementName, osNumber,
    1230             :                                          osAttributeName);
    1231             : 
    1232          83 :         bool bWillSkip = false;
    1233             :         // Handle Atom entries with elements with sub-elements like
    1234             :         // <author><name>...</name><uri>...</uri></author>
    1235          83 :         if (eFormat == GEORSS_ATOM)
    1236             :         {
    1237          52 :             for (unsigned int k = 0;
    1238          52 :                  apszAllowedATOMFieldNamesWithSubElements[k] != nullptr; k++)
    1239             :             {
    1240          74 :                 if (osElementName ==
    1241          42 :                         apszAllowedATOMFieldNamesWithSubElements[k] &&
    1242           5 :                     !osAttributeName.empty())
    1243             :                 {
    1244           5 :                     bWillSkip = true;
    1245           5 :                     if (pbUsed[i])
    1246           2 :                         break;
    1247             : 
    1248           3 :                     VSIFPrintfL(fp, "      <%s>\n", osElementName.c_str());
    1249             : 
    1250          23 :                     for (int j = i; j < nFieldCount; j++)
    1251             :                     {
    1252          20 :                         poFieldDefn = poFeatureDefn->GetFieldDefn(j);
    1253          20 :                         if (!poFeatureIn->IsFieldSetAndNotNull(j))
    1254           0 :                             continue;
    1255             : 
    1256          40 :                         std::string osElementName2;
    1257          40 :                         std::string osNumber2;
    1258          40 :                         std::string osAttributeName2;
    1259          20 :                         OGRGeoRSSLayerSplitComposedField(
    1260             :                             poFieldDefn->GetNameRef(), osElementName2,
    1261             :                             osNumber2, osAttributeName2);
    1262             : 
    1263          26 :                         if (osElementName2 == osElementName &&
    1264          26 :                             osNumber2 == osNumber && !osAttributeName2.empty())
    1265             :                         {
    1266           5 :                             pbUsed[j] = TRUE;
    1267             : 
    1268           5 :                             char *pszValue = OGRGetXML_UTF8_EscapedString(
    1269             :                                 poFeatureIn->GetFieldAsString(j));
    1270           5 :                             VSIFPrintfL(fp, "        <%s>%s</%s>\n",
    1271             :                                         osAttributeName2.c_str(), pszValue,
    1272             :                                         osAttributeName2.c_str());
    1273           5 :                             CPLFree(pszValue);
    1274             :                         }
    1275             :                     }
    1276             : 
    1277           3 :                     VSIFPrintfL(fp, "      </%s>\n", osElementName.c_str());
    1278             : 
    1279           3 :                     break;
    1280             :                 }
    1281             :             }
    1282             :         }
    1283             : 
    1284          83 :         if (bWillSkip)
    1285             :         {
    1286             :             // Do nothing
    1287             :         }
    1288          78 :         else if (eFormat == GEORSS_RSS && strcmp(pszName, "pubDate") == 0)
    1289             :         {
    1290          12 :             const OGRField *psField = poFeatureIn->GetRawFieldRef(i);
    1291          12 :             char *pszDate = OGRGetRFC822DateTime(psField);
    1292          12 :             VSIFPrintfL(fp, "      <%s>%s</%s>\n", pszName, pszDate, pszName);
    1293          12 :             CPLFree(pszDate);
    1294             :         }
    1295          66 :         else if (eFormat == GEORSS_ATOM && (strcmp(pszName, "updated") == 0 ||
    1296          14 :                                             strcmp(pszName, "published") == 0))
    1297             :         {
    1298           2 :             const OGRField *psField = poFeatureIn->GetRawFieldRef(i);
    1299           2 :             char *pszDate = OGRGetXMLDateTime(psField);
    1300           2 :             VSIFPrintfL(fp, "      <%s>%s</%s>\n", pszName, pszDate, pszName);
    1301           2 :             CPLFree(pszDate);
    1302             :         }
    1303          64 :         else if (strcmp(pszName, "dc_date") == 0)
    1304             :         {
    1305           0 :             const OGRField *psField = poFeatureIn->GetRawFieldRef(i);
    1306           0 :             char *pszDate = OGRGetXMLDateTime(psField);
    1307           0 :             VSIFPrintfL(fp, "      <%s>%s</%s>\n", "dc:date", pszDate,
    1308             :                         "dc:date");
    1309           0 :             CPLFree(pszDate);
    1310             :         }
    1311             :         // RSS fields with content and attributes.
    1312         154 :         else if (eFormat == GEORSS_RSS &&
    1313         129 :                  (osElementName == "category" || osElementName == "guid" ||
    1314          39 :                   osElementName == "source"))
    1315             :         {
    1316          12 :             if (osAttributeName.empty())
    1317             :             {
    1318           6 :                 OGRGeoRSSLayerWriteSimpleElement(
    1319             :                     fp, osElementName.c_str(), osNumber.c_str(),
    1320             :                     apszAllowedRSSFieldNames, poFeatureDefn, poFeatureIn);
    1321             :             }
    1322             :         }
    1323             :         // RSS field with attribute only.
    1324          52 :         else if (eFormat == GEORSS_RSS && osElementName == "enclosure")
    1325             :         {
    1326           0 :             if (osAttributeName == "url")
    1327             :             {
    1328           0 :                 OGRGeoRSSLayerWriteSimpleElement(
    1329             :                     fp, osElementName.c_str(), osNumber.c_str(),
    1330             :                     apszAllowedRSSFieldNames, poFeatureDefn, poFeatureIn);
    1331             :             }
    1332             :         }
    1333             :         /* ATOM fields with attribute only */
    1334          78 :         else if (eFormat == GEORSS_ATOM &&
    1335          26 :                  (osElementName == "category" || osElementName == "link"))
    1336             :         {
    1337          21 :             if ((osElementName == "category" && osAttributeName == "term") ||
    1338          14 :                 (osElementName == "link" && osAttributeName == "href"))
    1339             :             {
    1340           2 :                 OGRGeoRSSLayerWriteSimpleElement(
    1341             :                     fp, osElementName.c_str(), osNumber.c_str(),
    1342             :                     apszAllowedATOMFieldNames, poFeatureDefn, poFeatureIn);
    1343             :             }
    1344             :         }
    1345          45 :         else if (eFormat == GEORSS_ATOM && (STARTS_WITH(pszName, "content") ||
    1346           2 :                                             STARTS_WITH(pszName, "summary")))
    1347             :         {
    1348           4 :             if (strchr(pszName, '_') == nullptr)
    1349             :             {
    1350           1 :                 VSIFPrintfL(fp, "      <%s", pszName);
    1351             : 
    1352           1 :                 bool bIsXHTML = false;
    1353             :                 char *pszFieldName =
    1354           1 :                     CPLStrdup(CPLSPrintf("%s_%s", pszName, "type"));
    1355           1 :                 int iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
    1356           1 :                 if (iIndex != -1 && poFeatureIn->IsFieldSetAndNotNull(iIndex))
    1357             :                 {
    1358           1 :                     bIsXHTML = strcmp(poFeatureIn->GetFieldAsString(iIndex),
    1359             :                                       "xhtml") == 0;
    1360           1 :                     char *pszValue = OGRGetXML_UTF8_EscapedString(
    1361             :                         poFeatureIn->GetFieldAsString(iIndex));
    1362           1 :                     VSIFPrintfL(fp, " %s=\"%s\"", "type", pszValue);
    1363           1 :                     CPLFree(pszValue);
    1364             :                 }
    1365           1 :                 CPLFree(pszFieldName);
    1366             : 
    1367             :                 pszFieldName =
    1368           1 :                     CPLStrdup(CPLSPrintf("%s_%s", pszName, "xml_lang"));
    1369           1 :                 iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
    1370           1 :                 if (iIndex != -1 && poFeatureIn->IsFieldSetAndNotNull(iIndex))
    1371             :                 {
    1372           1 :                     char *pszValue = OGRGetXML_UTF8_EscapedString(
    1373             :                         poFeatureIn->GetFieldAsString(iIndex));
    1374           1 :                     VSIFPrintfL(fp, " %s=\"%s\"", "xml:lang", pszValue);
    1375           1 :                     CPLFree(pszValue);
    1376             :                 }
    1377           1 :                 CPLFree(pszFieldName);
    1378             : 
    1379             :                 pszFieldName =
    1380           1 :                     CPLStrdup(CPLSPrintf("%s_%s", pszName, "xml_base"));
    1381           1 :                 iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
    1382           1 :                 if (iIndex != -1 && poFeatureIn->IsFieldSetAndNotNull(iIndex))
    1383             :                 {
    1384           1 :                     char *pszValue = OGRGetXML_UTF8_EscapedString(
    1385             :                         poFeatureIn->GetFieldAsString(iIndex));
    1386           1 :                     VSIFPrintfL(fp, " %s=\"%s\"", "xml:base", pszValue);
    1387           1 :                     CPLFree(pszValue);
    1388             :                 }
    1389           1 :                 CPLFree(pszFieldName);
    1390             : 
    1391           1 :                 VSIFPrintfL(fp, ">");
    1392           1 :                 if (bIsXHTML)
    1393             :                 {
    1394           1 :                     VSIFPrintfL(fp, "%s", poFeatureIn->GetFieldAsString(i));
    1395             :                 }
    1396             :                 else
    1397             :                 {
    1398           0 :                     char *pszValue = OGRGetXML_UTF8_EscapedString(
    1399             :                         poFeatureIn->GetFieldAsString(i));
    1400           0 :                     VSIFPrintfL(fp, "%s", pszValue);
    1401           0 :                     CPLFree(pszValue);
    1402             :                 }
    1403           1 :                 VSIFPrintfL(fp, "      </%s>\n", pszName);
    1404           4 :             }
    1405             :         }
    1406          41 :         else if (STARTS_WITH(pszName, "dc_subject"))
    1407             :         {
    1408           0 :             if (strchr(pszName + strlen("dc_subject"), '_') == nullptr)
    1409             :             {
    1410           0 :                 VSIFPrintfL(fp, "      <%s", "dc:subject");
    1411             : 
    1412             :                 char *pszFieldName =
    1413           0 :                     CPLStrdup(CPLSPrintf("%s_%s", pszName, "xml_lang"));
    1414           0 :                 int iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
    1415           0 :                 if (iIndex != -1 && poFeatureIn->IsFieldSetAndNotNull(iIndex))
    1416             :                 {
    1417           0 :                     char *pszValue = OGRGetXML_UTF8_EscapedString(
    1418             :                         poFeatureIn->GetFieldAsString(iIndex));
    1419           0 :                     VSIFPrintfL(fp, " %s=\"%s\"", "xml:lang", pszValue);
    1420           0 :                     CPLFree(pszValue);
    1421             :                 }
    1422           0 :                 CPLFree(pszFieldName);
    1423             : 
    1424           0 :                 char *pszValue = OGRGetXML_UTF8_EscapedString(
    1425             :                     poFeatureIn->GetFieldAsString(i));
    1426           0 :                 VSIFPrintfL(fp, ">%s</%s>\n", pszValue, "dc:subject");
    1427           0 :                 CPLFree(pszValue);
    1428             :             }
    1429             :         }
    1430             :         else
    1431             :         {
    1432          41 :             char *pszTagName = CPLStrdup(pszName);
    1433          41 :             if (IsStandardField(pszName) == FALSE)
    1434             :             {
    1435           3 :                 int nCountUnderscore = 0;
    1436          29 :                 for (int j = 0; pszTagName[j] != 0; j++)
    1437             :                 {
    1438          26 :                     if (pszTagName[j] == '_')
    1439             :                     {
    1440           2 :                         if (nCountUnderscore == 0)
    1441           2 :                             pszTagName[j] = ':';
    1442           2 :                         nCountUnderscore++;
    1443             :                     }
    1444          24 :                     else if (pszTagName[j] == ' ')
    1445           0 :                         pszTagName[j] = '_';
    1446             :                 }
    1447           3 :                 if (nCountUnderscore == 0)
    1448             :                 {
    1449           1 :                     char *pszTemp = CPLStrdup(CPLSPrintf("ogr:%s", pszTagName));
    1450           1 :                     CPLFree(pszTagName);
    1451           1 :                     pszTagName = pszTemp;
    1452             :                 }
    1453             :             }
    1454             :             char *pszValue =
    1455          41 :                 OGRGetXML_UTF8_EscapedString(poFeatureIn->GetFieldAsString(i));
    1456          41 :             VSIFPrintfL(fp, "      <%s>%s</%s>\n", pszTagName, pszValue,
    1457             :                         pszTagName);
    1458          41 :             CPLFree(pszValue);
    1459          41 :             CPLFree(pszTagName);
    1460             :         }
    1461             :     }
    1462             : 
    1463          95 :     CPLFree(pbUsed);
    1464             : 
    1465          95 :     OGRGeoRSSGeomDialect eGeomDialect = poDS->GetGeomDialect();
    1466          95 :     OGRGeometry *poGeom = poFeatureIn->GetGeometryRef();
    1467          95 :     if (poGeom != nullptr && !poGeom->IsEmpty())
    1468             :     {
    1469          59 :         char *pszURN = nullptr;
    1470          59 :         bool bSwapCoordinates = false;
    1471          59 :         if (eGeomDialect == GEORSS_GML)
    1472             :         {
    1473           5 :             if (poSRS != nullptr)
    1474             :             {
    1475           1 :                 const char *pszAuthorityName = poSRS->GetAuthorityName(nullptr);
    1476           1 :                 const char *pszAuthorityCode = poSRS->GetAuthorityCode(nullptr);
    1477           1 :                 if (pszAuthorityName != nullptr &&
    1478           1 :                     EQUAL(pszAuthorityName, "EPSG") &&
    1479             :                     pszAuthorityCode != nullptr)
    1480             :                 {
    1481           1 :                     if (!EQUAL(pszAuthorityCode, "4326"))
    1482           1 :                         pszURN = CPLStrdup(CPLSPrintf(
    1483             :                             "urn:ogc:def:crs:EPSG::%s", pszAuthorityCode));
    1484             : 
    1485             :                     /* In case the SRS is a geographic SRS and that we have */
    1486             :                     /* no axis definition, we assume that the order is */
    1487             :                     /* lon/lat. */
    1488             :                     const char *pszAxisName =
    1489           1 :                         poSRS->GetAxis(nullptr, 0, nullptr);
    1490           1 :                     if (poSRS->IsGeographic() &&
    1491           0 :                         (pszAxisName == nullptr ||
    1492           0 :                          STARTS_WITH_CI(pszAxisName, "Lon")))
    1493             :                     {
    1494           0 :                         bSwapCoordinates = true;
    1495           1 :                     }
    1496             :                 }
    1497             :                 else
    1498             :                 {
    1499             :                     static bool bOnce = false;
    1500           0 :                     if (!bOnce)
    1501             :                     {
    1502           0 :                         bOnce = true;
    1503           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    1504             :                                  "Could not translate SRS into GML urn");
    1505             :                     }
    1506             :                 }
    1507             :             }
    1508             :             else
    1509             :             {
    1510           4 :                 bSwapCoordinates = true;
    1511             :             }
    1512             :         }
    1513             : 
    1514          59 :         char szCoord[75] = {};
    1515          59 :         switch (wkbFlatten(poGeom->getGeometryType()))
    1516             :         {
    1517          12 :             case wkbPoint:
    1518             :             {
    1519          12 :                 OGRPoint *poPoint = poGeom->toPoint();
    1520          12 :                 const double x = poPoint->getX();
    1521          12 :                 const double y = poPoint->getY();
    1522          12 :                 if (eGeomDialect == GEORSS_GML)
    1523             :                 {
    1524           2 :                     VSIFPrintfL(fp, "      <georss:where><gml:Point");
    1525           2 :                     if (pszURN != nullptr)
    1526           1 :                         VSIFPrintfL(fp, " srsName=\"%s\"", pszURN);
    1527           2 :                     if (poGeom->getCoordinateDimension() == 3)
    1528             :                     {
    1529           0 :                         OGRMakeWktCoordinate(szCoord, bSwapCoordinates ? y : x,
    1530             :                                              bSwapCoordinates ? x : y,
    1531             :                                              poPoint->getZ(), 3);
    1532           0 :                         VSIFPrintfL(fp, " srsDimension=\"3\"><gml:pos>%s",
    1533             :                                     szCoord);
    1534             :                     }
    1535             :                     else
    1536             :                     {
    1537           2 :                         OGRMakeWktCoordinate(szCoord, bSwapCoordinates ? y : x,
    1538             :                                              bSwapCoordinates ? x : y, 0, 2);
    1539           2 :                         VSIFPrintfL(fp, "><gml:pos>%s", szCoord);
    1540             :                     }
    1541           2 :                     VSIFPrintfL(fp, "</gml:pos></gml:Point></georss:where>\n");
    1542             :                 }
    1543          10 :                 else if (eGeomDialect == GEORSS_SIMPLE)
    1544             :                 {
    1545           9 :                     OGRMakeWktCoordinate(szCoord, y, x, 0, 2);
    1546           9 :                     VSIFPrintfL(fp, "      <georss:point>%s</georss:point>\n",
    1547             :                                 szCoord);
    1548             :                 }
    1549           1 :                 else if (eGeomDialect == GEORSS_W3C_GEO)
    1550             :                 {
    1551           1 :                     OGRFormatDouble(szCoord, sizeof(szCoord), y, '.');
    1552           1 :                     VSIFPrintfL(fp, "      <geo:lat>%s</geo:lat>\n", szCoord);
    1553           1 :                     OGRFormatDouble(szCoord, sizeof(szCoord), x, '.');
    1554           1 :                     VSIFPrintfL(fp, "      <geo:long>%s</geo:long>\n", szCoord);
    1555             :                 }
    1556          12 :                 break;
    1557             :             }
    1558             : 
    1559          11 :             case wkbLineString:
    1560             :             {
    1561          11 :                 OGRLineString *poLineString = poGeom->toLineString();
    1562          11 :                 if (eGeomDialect == GEORSS_GML)
    1563             :                 {
    1564           1 :                     VSIFPrintfL(fp, "      <georss:where><gml:LineString");
    1565           1 :                     if (pszURN != nullptr)
    1566           0 :                         VSIFPrintfL(fp, " srsName=\"%s\"", pszURN);
    1567           1 :                     VSIFPrintfL(fp, "><gml:posList>\n");
    1568           1 :                     const int n = poLineString->getNumPoints();
    1569           4 :                     for (int i = 0; i < n; i++)
    1570             :                     {
    1571           3 :                         const double x = poLineString->getX(i);
    1572           3 :                         const double y = poLineString->getY(i);
    1573           3 :                         OGRMakeWktCoordinate(szCoord, bSwapCoordinates ? y : x,
    1574             :                                              bSwapCoordinates ? x : y, 0, 2);
    1575           3 :                         VSIFPrintfL(fp, "%s ", szCoord);
    1576             :                     }
    1577           1 :                     VSIFPrintfL(
    1578             :                         fp, "</gml:posList></gml:LineString></georss:where>\n");
    1579             :                 }
    1580          10 :                 else if (eGeomDialect == GEORSS_SIMPLE)
    1581             :                 {
    1582           9 :                     VSIFPrintfL(fp, "      <georss:line>\n");
    1583           9 :                     int n = poLineString->getNumPoints();
    1584          28 :                     for (int i = 0; i < n; i++)
    1585             :                     {
    1586          19 :                         double x = poLineString->getX(i);
    1587          19 :                         double y = poLineString->getY(i);
    1588          19 :                         OGRMakeWktCoordinate(szCoord, y, x, 0, 2);
    1589          19 :                         VSIFPrintfL(fp, "%s ", szCoord);
    1590             :                     }
    1591           9 :                     VSIFPrintfL(fp, "</georss:line>\n");
    1592             :                 }
    1593             :                 else
    1594             :                 {
    1595             :                     // Not supported.
    1596             :                 }
    1597          11 :                 break;
    1598             :             }
    1599             : 
    1600          12 :             case wkbPolygon:
    1601             :             {
    1602          12 :                 OGRPolygon *poPolygon = poGeom->toPolygon();
    1603          12 :                 OGRLineString *poLineString = poPolygon->getExteriorRing();
    1604          12 :                 if (poLineString == nullptr)
    1605           0 :                     break;
    1606             : 
    1607          12 :                 if (eGeomDialect == GEORSS_GML)
    1608             :                 {
    1609           2 :                     VSIFPrintfL(fp, "      <georss:where><gml:Polygon");
    1610           2 :                     if (pszURN != nullptr)
    1611           0 :                         VSIFPrintfL(fp, " srsName=\"%s\"", pszURN);
    1612           2 :                     VSIFPrintfL(
    1613             :                         fp, "><gml:exterior><gml:LinearRing><gml:posList>\n");
    1614           2 :                     const int n = poLineString->getNumPoints();
    1615          12 :                     for (int i = 0; i < n; i++)
    1616             :                     {
    1617          10 :                         const double x = poLineString->getX(i);
    1618          10 :                         const double y = poLineString->getY(i);
    1619          10 :                         OGRMakeWktCoordinate(szCoord, bSwapCoordinates ? y : x,
    1620             :                                              bSwapCoordinates ? x : y, 0, 2);
    1621          10 :                         VSIFPrintfL(fp, "%s ", szCoord);
    1622             :                     }
    1623           2 :                     VSIFPrintfL(fp,
    1624             :                                 "</gml:posList></gml:LinearRing></gml:exterior>"
    1625             :                                 "</gml:Polygon></georss:where>\n");
    1626             :                 }
    1627          10 :                 else if (eGeomDialect == GEORSS_SIMPLE)
    1628             :                 {
    1629           8 :                     VSIFPrintfL(fp, "      <georss:polygon>\n");
    1630           8 :                     const int n = poLineString->getNumPoints();
    1631          48 :                     for (int i = 0; i < n; i++)
    1632             :                     {
    1633          40 :                         const double x = poLineString->getX(i);
    1634          40 :                         const double y = poLineString->getY(i);
    1635          40 :                         OGRMakeWktCoordinate(szCoord, y, x, 0, 2);
    1636          40 :                         VSIFPrintfL(fp, "%s ", szCoord);
    1637             :                     }
    1638           8 :                     VSIFPrintfL(fp, "</georss:polygon>\n");
    1639             :                 }
    1640             :                 else
    1641             :                 {
    1642             :                     /* Not supported */
    1643             :                 }
    1644          12 :                 break;
    1645             :             }
    1646             : 
    1647          24 :             default:
    1648             :                 /* Not supported */
    1649          24 :                 break;
    1650             :         }
    1651          59 :         CPLFree(pszURN);
    1652             :     }
    1653             : 
    1654          95 :     if (eFormat == GEORSS_RSS)
    1655          94 :         VSIFPrintfL(fp, "    </item>\n");
    1656             :     else
    1657           1 :         VSIFPrintfL(fp, "    </entry>\n");
    1658             : 
    1659          95 :     return OGRERR_NONE;
    1660             : }
    1661             : 
    1662             : /************************************************************************/
    1663             : /*                            CreateField()                             */
    1664             : /************************************************************************/
    1665             : 
    1666         146 : OGRErr OGRGeoRSSLayer::CreateField(const OGRFieldDefn *poFieldDefn,
    1667             :                                    CPL_UNUSED int bApproxOK)
    1668             : {
    1669         146 :     const char *pszName = poFieldDefn->GetNameRef();
    1670         126 :     if (((eFormat == GEORSS_RSS && strcmp(pszName, "pubDate") == 0) ||
    1671         143 :          (eFormat == GEORSS_ATOM && (strcmp(pszName, "updated") == 0 ||
    1672          19 :                                      strcmp(pszName, "published") == 0)) ||
    1673         292 :          strcmp(pszName, "dc:date") == 0) &&
    1674           5 :         poFieldDefn->GetType() != OFTDateTime)
    1675             :     {
    1676           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s",
    1677             :                  pszName);
    1678           0 :         return OGRERR_FAILURE;
    1679             :     }
    1680             : 
    1681         447 :     for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++)
    1682             :     {
    1683         301 :         if (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(),
    1684         301 :                    pszName) == 0)
    1685             :         {
    1686           0 :             return OGRERR_FAILURE;
    1687             :         }
    1688             :     }
    1689             : 
    1690         146 :     if (IsStandardField(pszName))
    1691             :     {
    1692          47 :         poFeatureDefn->AddFieldDefn(poFieldDefn);
    1693          47 :         return OGRERR_NONE;
    1694             :     }
    1695             : 
    1696          99 :     if (poDS->GetUseExtensions() == FALSE)
    1697             :     {
    1698          96 :         CPLError(
    1699             :             CE_Failure, CPLE_NotSupported,
    1700             :             "Field of name '%s' is not supported in %s schema. "
    1701             :             "Use USE_EXTENSIONS creation option to allow use of extensions.",
    1702          96 :             pszName, (eFormat == GEORSS_RSS) ? "RSS" : "ATOM");
    1703          96 :         return OGRERR_FAILURE;
    1704             :     }
    1705             :     else
    1706             :     {
    1707           3 :         poFeatureDefn->AddFieldDefn(poFieldDefn);
    1708           3 :         return OGRERR_NONE;
    1709             :     }
    1710             : }
    1711             : 
    1712             : #ifdef HAVE_EXPAT
    1713             : 
    1714         304 : static void XMLCALL startElementLoadSchemaCbk(void *pUserData,
    1715             :                                               const char *pszName,
    1716             :                                               const char **ppszAttr)
    1717             : {
    1718         304 :     ((OGRGeoRSSLayer *)pUserData)->startElementLoadSchemaCbk(pszName, ppszAttr);
    1719         304 : }
    1720             : 
    1721         304 : static void XMLCALL endElementLoadSchemaCbk(void *pUserData,
    1722             :                                             const char *pszName)
    1723             : {
    1724         304 :     ((OGRGeoRSSLayer *)pUserData)->endElementLoadSchemaCbk(pszName);
    1725         304 : }
    1726             : 
    1727         889 : static void XMLCALL dataHandlerLoadSchemaCbk(void *pUserData, const char *data,
    1728             :                                              int nLen)
    1729             : {
    1730         889 :     ((OGRGeoRSSLayer *)pUserData)->dataHandlerLoadSchemaCbk(data, nLen);
    1731         889 : }
    1732             : 
    1733             : /************************************************************************/
    1734             : /*                             LoadSchema()                             */
    1735             : /************************************************************************/
    1736             : 
    1737             : /** This function parses the whole file to detect the fields */
    1738          67 : void OGRGeoRSSLayer::LoadSchema()
    1739             : {
    1740          67 :     if (bHasReadSchema)
    1741          54 :         return;
    1742             : 
    1743          67 :     bHasReadSchema = true;
    1744             : 
    1745          67 :     if (fpGeoRSS == nullptr)
    1746          54 :         return;
    1747             : 
    1748          13 :     oSchemaParser = OGRCreateExpatXMLParser();
    1749          13 :     XML_SetElementHandler(oSchemaParser, ::startElementLoadSchemaCbk,
    1750             :                           ::endElementLoadSchemaCbk);
    1751          13 :     XML_SetCharacterDataHandler(oSchemaParser, ::dataHandlerLoadSchemaCbk);
    1752          13 :     XML_SetUserData(oSchemaParser, this);
    1753             : 
    1754          13 :     VSIFSeekL(fpGeoRSS, 0, SEEK_SET);
    1755             : 
    1756          13 :     bInFeature = false;
    1757          13 :     currentDepth = 0;
    1758          13 :     currentFieldDefn = nullptr;
    1759          13 :     pszSubElementName = nullptr;
    1760          13 :     pszSubElementValue = nullptr;
    1761          13 :     nSubElementValueLen = 0;
    1762          13 :     this->bSameSRS = true;
    1763          13 :     CPLFree(this->pszGMLSRSName);
    1764          13 :     this->pszGMLSRSName = nullptr;
    1765          13 :     this->eGeomType = wkbUnknown;
    1766          13 :     this->bFoundGeom = false;
    1767          13 :     bInTagWithSubTag = false;
    1768          13 :     pszTagWithSubTag = nullptr;
    1769          13 :     bStopParsing = false;
    1770          13 :     nWithoutEventCounter = 0;
    1771          13 :     nTotalFeatureCount = 0;
    1772          13 :     setOfFoundFields = nullptr;
    1773             : 
    1774          13 :     std::vector<char> aBuf(PARSER_BUF_SIZE);
    1775          13 :     int nDone = 0;
    1776           0 :     do
    1777             :     {
    1778          13 :         nDataHandlerCounter = 0;
    1779             :         unsigned int nLen =
    1780          13 :             (unsigned int)VSIFReadL(aBuf.data(), 1, aBuf.size(), fpGeoRSS);
    1781          13 :         nDone = nLen < aBuf.size();
    1782          13 :         if (XML_Parse(oSchemaParser, aBuf.data(), nLen, nDone) ==
    1783             :             XML_STATUS_ERROR)
    1784             :         {
    1785           0 :             CPLError(
    1786             :                 CE_Failure, CPLE_AppDefined,
    1787             :                 "XML parsing of GeoRSS file failed : %s at line %d, column %d",
    1788             :                 XML_ErrorString(XML_GetErrorCode(oSchemaParser)),
    1789           0 :                 static_cast<int>(XML_GetCurrentLineNumber(oSchemaParser)),
    1790           0 :                 static_cast<int>(XML_GetCurrentColumnNumber(oSchemaParser)));
    1791           0 :             bStopParsing = true;
    1792             :         }
    1793          13 :         nWithoutEventCounter++;
    1794          13 :     } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
    1795             : 
    1796          13 :     XML_ParserFree(oSchemaParser);
    1797             : 
    1798          13 :     if (nWithoutEventCounter == 10)
    1799             :     {
    1800           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1801             :                  "Too much data inside one element. File probably corrupted");
    1802           0 :         bStopParsing = true;
    1803             :     }
    1804             : 
    1805          13 :     CPLAssert(poSRS == nullptr);
    1806          13 :     if (this->bSameSRS && this->bFoundGeom)
    1807             :     {
    1808           9 :         if (this->pszGMLSRSName == nullptr)
    1809             :         {
    1810           8 :             poSRS = new OGRSpatialReference();
    1811           8 :             poSRS->SetWellKnownGeogCS("WGS84");
    1812           8 :             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1813             :         }
    1814             :         else
    1815             :         {
    1816           1 :             poSRS = new OGRSpatialReference();
    1817           1 :             poSRS->importFromURN(pszGMLSRSName);
    1818             :         }
    1819             :     }
    1820             : 
    1821          13 :     if (this->eGeomType != wkbUnknown)
    1822           5 :         poFeatureDefn->SetGeomType(eGeomType);
    1823          13 :     if (poFeatureDefn->GetGeomFieldCount() != 0)
    1824          13 :         poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
    1825             : 
    1826          13 :     if (setOfFoundFields)
    1827          13 :         CPLHashSetDestroy(setOfFoundFields);
    1828          13 :     setOfFoundFields = nullptr;
    1829          13 :     CPLFree(pszGMLSRSName);
    1830          13 :     pszGMLSRSName = nullptr;
    1831          13 :     CPLFree(pszTagWithSubTag);
    1832          13 :     pszTagWithSubTag = nullptr;
    1833             : 
    1834          13 :     VSIFSeekL(fpGeoRSS, 0, SEEK_SET);
    1835             : }
    1836             : 
    1837             : /************************************************************************/
    1838             : /*                     startElementLoadSchemaCbk()                      */
    1839             : /************************************************************************/
    1840             : 
    1841         304 : void OGRGeoRSSLayer::startElementLoadSchemaCbk(const char *pszName,
    1842             :                                                const char **ppszAttr)
    1843             : {
    1844         304 :     if (bStopParsing)
    1845           0 :         return;
    1846             : 
    1847         304 :     nWithoutEventCounter = 0;
    1848         304 :     const char *pszNoNSName = pszName;
    1849         304 :     const char *pszColon = strchr(pszNoNSName, ':');
    1850         304 :     if (pszColon)
    1851          77 :         pszNoNSName = pszColon + 1;
    1852             : 
    1853         304 :     if ((eFormat == GEORSS_ATOM && currentDepth == 1 &&
    1854          23 :          strcmp(pszNoNSName, "entry") == 0) ||
    1855         301 :         ((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) && !bInFeature &&
    1856          68 :          (currentDepth == 1 || currentDepth == 2) &&
    1857          58 :          strcmp(pszNoNSName, "item") == 0))
    1858             :     {
    1859          28 :         bInFeature = true;
    1860          28 :         featureDepth = currentDepth;
    1861             : 
    1862          28 :         nTotalFeatureCount++;
    1863             : 
    1864          28 :         if (setOfFoundFields)
    1865          15 :             CPLHashSetDestroy(setOfFoundFields);
    1866          28 :         setOfFoundFields =
    1867          28 :             CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, CPLFree);
    1868             :     }
    1869         276 :     else if (bInTagWithSubTag && currentDepth == 3)
    1870             :     {
    1871             :         char *pszFieldName =
    1872          15 :             CPLStrdup(CPLSPrintf("%s_%s", pszTagWithSubTag, pszNoNSName));
    1873          15 :         if (poFeatureDefn->GetFieldIndex(pszFieldName) == -1)
    1874             :         {
    1875          30 :             OGRFieldDefn newFieldDefn(pszFieldName, OFTString);
    1876          15 :             poFeatureDefn->AddFieldDefn(&newFieldDefn);
    1877             : 
    1878          15 :             if (poFeatureDefn->GetFieldCount() == 100)
    1879             :             {
    1880           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1881             :                          "Too many fields. File probably corrupted");
    1882           0 :                 XML_StopParser(oSchemaParser, XML_FALSE);
    1883           0 :                 bStopParsing = true;
    1884             :             }
    1885             :         }
    1886          15 :         CPLFree(pszFieldName);
    1887             :     }
    1888         291 :     else if (bInFeature && eFormat == GEORSS_ATOM && currentDepth == 2 &&
    1889          30 :              OGRGeoRSSLayerATOMTagHasSubElement(pszNoNSName))
    1890             :     {
    1891           9 :         CPLFree(pszTagWithSubTag);
    1892           9 :         pszTagWithSubTag = CPLStrdup(pszNoNSName);
    1893             : 
    1894           9 :         int count = 1;
    1895          12 :         while (CPLHashSetLookup(setOfFoundFields, pszTagWithSubTag) != nullptr)
    1896             :         {
    1897           3 :             count++;
    1898           3 :             CPLFree(pszTagWithSubTag);
    1899           3 :             pszTagWithSubTag =
    1900           3 :                 CPLStrdup(CPLSPrintf("%s%d", pszNoNSName, count));
    1901           3 :             if (pszTagWithSubTag[0] == 0)
    1902             :             {
    1903           0 :                 XML_StopParser(oSchemaParser, XML_FALSE);
    1904           0 :                 bStopParsing = true;
    1905           0 :                 break;
    1906             :             }
    1907             :         }
    1908           9 :         CPLHashSetInsert(setOfFoundFields, CPLStrdup(pszTagWithSubTag));
    1909             : 
    1910           9 :         bInTagWithSubTag = true;
    1911             :     }
    1912         252 :     else if (bInFeature && currentDepth == featureDepth + 1 &&
    1913         149 :              !IS_GEO_ELEMENT(pszName))
    1914             :     {
    1915         127 :         if (pszName != pszNoNSName && STARTS_WITH(pszName, "atom:"))
    1916           7 :             pszName = pszNoNSName;
    1917             : 
    1918         127 :         CPLFree(pszSubElementName);
    1919         127 :         pszSubElementName = CPLStrdup(pszName);
    1920             : 
    1921         127 :         int count = 1;
    1922         135 :         while (CPLHashSetLookup(setOfFoundFields, pszSubElementName) != nullptr)
    1923             :         {
    1924           8 :             count++;
    1925           8 :             CPLFree(pszSubElementName);
    1926           8 :             pszSubElementName = CPLStrdup(CPLSPrintf("%s%d", pszName, count));
    1927             :         }
    1928         127 :         CPLHashSetInsert(setOfFoundFields, CPLStrdup(pszSubElementName));
    1929             : 
    1930             :         // Create field definition for element.
    1931             :         char *pszCompatibleName =
    1932         127 :             OGRGeoRSS_GetOGRCompatibleTagName(pszSubElementName);
    1933         127 :         int iField = poFeatureDefn->GetFieldIndex(pszCompatibleName);
    1934         127 :         if (iField >= 0)
    1935             :         {
    1936          66 :             currentFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1937             :         }
    1938          61 :         else if (!((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) &&
    1939          40 :                    strcmp(pszNoNSName, "enclosure") == 0) &&
    1940          61 :                  !(eFormat == GEORSS_ATOM &&
    1941          21 :                    strcmp(pszNoNSName, "link") == 0) &&
    1942          55 :                  !(eFormat == GEORSS_ATOM &&
    1943          15 :                    strcmp(pszNoNSName, "category") == 0))
    1944             :         {
    1945             :             OGRFieldType eFieldType;
    1946          55 :             if (((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) &&
    1947          40 :                  strcmp(pszNoNSName, "pubDate") == 0) ||
    1948          49 :                 (eFormat == GEORSS_ATOM &&
    1949          15 :                  strcmp(pszNoNSName, "updated") == 0) ||
    1950          46 :                 (eFormat == GEORSS_ATOM &&
    1951          12 :                  strcmp(pszNoNSName, "published") == 0) ||
    1952          43 :                 strcmp(pszName, "dc:date") == 0)
    1953          12 :                 eFieldType = OFTDateTime;
    1954             :             else
    1955          43 :                 eFieldType = OFTInteger;
    1956             : 
    1957         110 :             OGRFieldDefn newFieldDefn(pszCompatibleName, eFieldType);
    1958          55 :             poFeatureDefn->AddFieldDefn(&newFieldDefn);
    1959          55 :             currentFieldDefn =
    1960          55 :                 poFeatureDefn->GetFieldDefn(poFeatureDefn->GetFieldCount() - 1);
    1961             : 
    1962          55 :             if (poFeatureDefn->GetFieldCount() == 100)
    1963             :             {
    1964           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1965             :                          "Too many fields. File probably corrupted");
    1966           0 :                 XML_StopParser(oSchemaParser, XML_FALSE);
    1967           0 :                 bStopParsing = true;
    1968             :             }
    1969             :         }
    1970             : 
    1971             :         // Create field definitions for attributes.
    1972         168 :         for (int i = 0; ppszAttr[i] != nullptr && ppszAttr[i + 1] != nullptr &&
    1973          41 :                         !bStopParsing;
    1974          41 :              i += 2)
    1975             :         {
    1976          41 :             char *pszAttrCompatibleName = OGRGeoRSS_GetOGRCompatibleTagName(
    1977          41 :                 CPLSPrintf("%s_%s", pszSubElementName, ppszAttr[i]));
    1978          41 :             iField = poFeatureDefn->GetFieldIndex(pszAttrCompatibleName);
    1979          41 :             OGRFieldDefn *currentAttrFieldDefn = nullptr;
    1980          41 :             if (iField >= 0)
    1981             :             {
    1982           0 :                 currentAttrFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    1983             :             }
    1984             :             else
    1985             :             {
    1986          82 :                 OGRFieldDefn newFieldDefn(pszAttrCompatibleName, OFTInteger);
    1987          41 :                 poFeatureDefn->AddFieldDefn(&newFieldDefn);
    1988         123 :                 currentAttrFieldDefn = poFeatureDefn->GetFieldDefn(
    1989          41 :                     poFeatureDefn->GetFieldCount() - 1);
    1990             : 
    1991          41 :                 if (poFeatureDefn->GetFieldCount() == 100)
    1992             :                 {
    1993           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1994             :                              "Too many fields. File probably corrupted");
    1995           0 :                     XML_StopParser(oSchemaParser, XML_FALSE);
    1996           0 :                     bStopParsing = true;
    1997             :                 }
    1998             :             }
    1999          41 :             if (currentAttrFieldDefn->GetType() == OFTInteger ||
    2000           0 :                 currentAttrFieldDefn->GetType() == OFTReal)
    2001             :             {
    2002          41 :                 const CPLValueType eType = CPLGetValueType(ppszAttr[i + 1]);
    2003          41 :                 if (eType == CPL_VALUE_REAL)
    2004             :                 {
    2005           0 :                     currentAttrFieldDefn->SetType(OFTReal);
    2006             :                 }
    2007          41 :                 else if (eType == CPL_VALUE_STRING)
    2008             :                 {
    2009          38 :                     currentAttrFieldDefn->SetType(OFTString);
    2010             :                 }
    2011             :             }
    2012          41 :             CPLFree(pszAttrCompatibleName);
    2013             :         }
    2014             : 
    2015         127 :         CPLFree(pszCompatibleName);
    2016             :     }
    2017         125 :     else if (strcmp(pszName, "georss:point") == 0 ||
    2018         122 :              strcmp(pszName, "georss:line") == 0 ||
    2019         120 :              strcmp(pszName, "geo:line") == 0 || IS_LAT_ELEMENT(pszName) ||
    2020         119 :              strcmp(pszName, "georss:polygon") == 0 ||
    2021         116 :              strcmp(pszName, "georss:box") == 0)
    2022             :     {
    2023          11 :         if (bSameSRS)
    2024             :         {
    2025          11 :             if (pszGMLSRSName != nullptr)
    2026           0 :                 bSameSRS = false;
    2027             :         }
    2028             :     }
    2029         114 :     else if (strcmp(pszName, "gml:Point") == 0 ||
    2030         111 :              strcmp(pszName, "gml:LineString") == 0 ||
    2031         108 :              strcmp(pszName, "gml:Polygon") == 0 ||
    2032         105 :              strcmp(pszName, "gml:MultiPoint") == 0 ||
    2033         105 :              strcmp(pszName, "gml:MultiLineString") == 0 ||
    2034         105 :              strcmp(pszName, "gml:MultiPolygon") == 0 ||
    2035         105 :              strcmp(pszName, "gml:Envelope") == 0)
    2036             :     {
    2037          10 :         if (bSameSRS)
    2038             :         {
    2039          10 :             bool bFoundSRS = false;
    2040          10 :             for (int i = 0; ppszAttr[i] != nullptr; i += 2)
    2041             :             {
    2042           1 :                 if (strcmp(ppszAttr[i], "srsName") == 0)
    2043             :                 {
    2044           1 :                     bFoundSRS = true;
    2045           1 :                     if (pszGMLSRSName != nullptr)
    2046             :                     {
    2047           0 :                         if (strcmp(pszGMLSRSName, ppszAttr[i + 1]) != 0)
    2048           0 :                             bSameSRS = false;
    2049             :                     }
    2050             :                     else
    2051           1 :                         pszGMLSRSName = CPLStrdup(ppszAttr[i + 1]);
    2052           1 :                     break;
    2053             :                 }
    2054             :             }
    2055          10 :             if (!bFoundSRS && pszGMLSRSName != nullptr)
    2056           0 :                 bSameSRS = false;
    2057             :         }
    2058             :     }
    2059             : 
    2060         304 :     if (!bInFeature || currentDepth >= featureDepth + 1)
    2061             :     {
    2062         276 :         int nDimension = 2;
    2063         367 :         for (int i = 0; ppszAttr[i] != nullptr && ppszAttr[i + 1] != nullptr;
    2064          91 :              i += 2)
    2065             :         {
    2066          91 :             if (strcmp(ppszAttr[i], "srsDimension") == 0)
    2067             :             {
    2068           0 :                 nDimension = atoi(ppszAttr[i + 1]);
    2069           0 :                 break;
    2070             :             }
    2071             :         }
    2072             : 
    2073         276 :         OGRwkbGeometryType eFoundGeomType = wkbUnknown;
    2074         276 :         if (strcmp(pszName, "georss:point") == 0 || IS_LAT_ELEMENT(pszName) ||
    2075         272 :             strcmp(pszName, "gml:Point") == 0)
    2076             :         {
    2077           7 :             eFoundGeomType = wkbPoint;
    2078             :         }
    2079         269 :         else if (strcmp(pszName, "gml:MultiPoint") == 0)
    2080             :         {
    2081           0 :             eFoundGeomType = wkbMultiPoint;
    2082             :         }
    2083         269 :         else if (strcmp(pszName, "georss:line") == 0 ||
    2084         267 :                  strcmp(pszName, "geo:line") == 0 ||
    2085         267 :                  strcmp(pszName, "gml:LineString") == 0)
    2086             :         {
    2087           5 :             eFoundGeomType = wkbLineString;
    2088             :         }
    2089         264 :         else if (strcmp(pszName, "gml:MultiLineString") == 0)
    2090             :         {
    2091           0 :             eFoundGeomType = wkbMultiLineString;
    2092             :         }
    2093         264 :         else if (strcmp(pszName, "georss:polygon") == 0 ||
    2094         261 :                  strcmp(pszName, "gml:Polygon") == 0 ||
    2095         258 :                  strcmp(pszName, "gml:Envelope") == 0 ||
    2096         257 :                  strcmp(pszName, "georss:box") == 0)
    2097             :         {
    2098           9 :             eFoundGeomType = wkbPolygon;
    2099             :         }
    2100         255 :         else if (strcmp(pszName, "gml:MultiPolygon") == 0)
    2101             :         {
    2102           0 :             eFoundGeomType = wkbMultiPolygon;
    2103             :         }
    2104             : 
    2105         276 :         if (eFoundGeomType != wkbUnknown)
    2106             :         {
    2107          21 :             if (!bFoundGeom)
    2108             :             {
    2109           9 :                 eGeomType = eFoundGeomType;
    2110           9 :                 bFoundGeom = true;
    2111             :             }
    2112          12 :             else if (wkbFlatten(eGeomType) != eFoundGeomType)
    2113             :             {
    2114          12 :                 eGeomType = wkbUnknown;
    2115             :             }
    2116             : 
    2117          21 :             if (nDimension == 3)
    2118           0 :                 eGeomType = wkbSetZ(eGeomType);
    2119             :         }
    2120             :     }
    2121             : 
    2122         304 :     currentDepth++;
    2123             : }
    2124             : 
    2125             : /************************************************************************/
    2126             : /*                      endElementLoadSchemaCbk()                       */
    2127             : /************************************************************************/
    2128             : 
    2129         304 : void OGRGeoRSSLayer::endElementLoadSchemaCbk(const char *pszName)
    2130             : {
    2131         304 :     if (bStopParsing)
    2132           0 :         return;
    2133             : 
    2134         304 :     nWithoutEventCounter = 0;
    2135             : 
    2136         304 :     currentDepth--;
    2137             : 
    2138         304 :     if (!bInFeature)
    2139          67 :         return;
    2140             : 
    2141         237 :     const char *pszNoNSName = pszName;
    2142         237 :     const char *pszColon = strchr(pszNoNSName, ':');
    2143         237 :     if (pszColon)
    2144          68 :         pszNoNSName = pszColon + 1;
    2145             : 
    2146         237 :     if ((eFormat == GEORSS_ATOM && currentDepth == 1 &&
    2147           3 :          strcmp(pszNoNSName, "entry") == 0) ||
    2148         234 :         ((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) &&
    2149         180 :          (currentDepth == 1 || currentDepth == 2) &&
    2150          25 :          strcmp(pszNoNSName, "item") == 0))
    2151             :     {
    2152          28 :         bInFeature = false;
    2153             :     }
    2154         239 :     else if (eFormat == GEORSS_ATOM && currentDepth == 2 &&
    2155          30 :              OGRGeoRSSLayerATOMTagHasSubElement(pszNoNSName))
    2156             :     {
    2157           9 :         bInTagWithSubTag = false;
    2158             :     }
    2159         200 :     else if (currentDepth == featureDepth + 1 && pszSubElementName)
    2160             :     {
    2161             :         // Patch field type.
    2162         127 :         if (pszSubElementValue && nSubElementValueLen && currentFieldDefn)
    2163             :         {
    2164         121 :             pszSubElementValue[nSubElementValueLen] = 0;
    2165         199 :             if (currentFieldDefn->GetType() == OFTInteger ||
    2166          78 :                 currentFieldDefn->GetType() == OFTReal)
    2167             :             {
    2168          43 :                 const CPLValueType eType = CPLGetValueType(pszSubElementValue);
    2169          43 :                 if (eType == CPL_VALUE_REAL)
    2170             :                 {
    2171           0 :                     currentFieldDefn->SetType(OFTReal);
    2172             :                 }
    2173          43 :                 else if (eType == CPL_VALUE_STRING)
    2174             :                 {
    2175          42 :                     currentFieldDefn->SetType(OFTString);
    2176             :                 }
    2177             :             }
    2178             :         }
    2179             : 
    2180         127 :         CPLFree(pszSubElementName);
    2181         127 :         pszSubElementName = nullptr;
    2182         127 :         CPLFree(pszSubElementValue);
    2183         127 :         pszSubElementValue = nullptr;
    2184         127 :         nSubElementValueLen = 0;
    2185         127 :         currentFieldDefn = nullptr;
    2186             :     }
    2187             : }
    2188             : 
    2189             : /************************************************************************/
    2190             : /*                      dataHandlerLoadSchemaCbk()                      */
    2191             : /************************************************************************/
    2192             : 
    2193         889 : void OGRGeoRSSLayer::dataHandlerLoadSchemaCbk(const char *data, int nLen)
    2194             : {
    2195         889 :     if (bStopParsing)
    2196           0 :         return;
    2197             : 
    2198         889 :     nDataHandlerCounter++;
    2199         889 :     if (nDataHandlerCounter >= PARSER_BUF_SIZE)
    2200             :     {
    2201           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2202             :                  "File probably corrupted (million laugh pattern)");
    2203           0 :         XML_StopParser(oSchemaParser, XML_FALSE);
    2204           0 :         bStopParsing = true;
    2205           0 :         return;
    2206             :     }
    2207             : 
    2208         889 :     nWithoutEventCounter = 0;
    2209             : 
    2210         889 :     if (pszSubElementName)
    2211             :     {
    2212         138 :         char *pszNewSubElementValue = static_cast<char *>(VSI_REALLOC_VERBOSE(
    2213             :             pszSubElementValue, nSubElementValueLen + nLen + 1));
    2214         138 :         if (pszNewSubElementValue == nullptr)
    2215             :         {
    2216           0 :             XML_StopParser(oSchemaParser, XML_FALSE);
    2217           0 :             bStopParsing = true;
    2218           0 :             return;
    2219             :         }
    2220         138 :         pszSubElementValue = pszNewSubElementValue;
    2221         138 :         memcpy(pszSubElementValue + nSubElementValueLen, data, nLen);
    2222         138 :         nSubElementValueLen += nLen;
    2223         138 :         if (nSubElementValueLen > 100000)
    2224             :         {
    2225           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2226             :                      "Too much data inside one element. "
    2227             :                      "File probably corrupted");
    2228           0 :             XML_StopParser(oSchemaParser, XML_FALSE);
    2229           0 :             bStopParsing = true;
    2230             :         }
    2231             :     }
    2232             : }
    2233             : #else
    2234             : void OGRGeoRSSLayer::LoadSchema()
    2235             : {
    2236             : }
    2237             : #endif
    2238             : 
    2239             : /************************************************************************/
    2240             : /*                           TestCapability()                           */
    2241             : /************************************************************************/
    2242             : 
    2243         108 : int OGRGeoRSSLayer::TestCapability(const char *pszCap) const
    2244             : 
    2245             : {
    2246         108 :     if (EQUAL(pszCap, OLCFastFeatureCount))
    2247           0 :         return !bWriteMode && bHasReadSchema && m_poFilterGeom == nullptr &&
    2248           0 :                m_poAttrQuery == nullptr;
    2249             : 
    2250         108 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
    2251           0 :         return TRUE;
    2252             : 
    2253         108 :     else if (EQUAL(pszCap, OLCSequentialWrite))
    2254          16 :         return bWriteMode;
    2255          92 :     else if (EQUAL(pszCap, OLCCreateField))
    2256          16 :         return bWriteMode;
    2257          76 :     else if (EQUAL(pszCap, OLCZGeometries))
    2258           0 :         return TRUE;
    2259             :     else
    2260          76 :         return FALSE;
    2261             : }
    2262             : 
    2263             : /************************************************************************/
    2264             : /*                          GetFeatureCount()                           */
    2265             : /************************************************************************/
    2266             : 
    2267           0 : GIntBig OGRGeoRSSLayer::GetFeatureCount(int bForce)
    2268             : 
    2269             : {
    2270           0 :     if (bWriteMode)
    2271             :     {
    2272           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2273             :                  "Cannot read features when writing a GeoRSS file");
    2274           0 :         return 0;
    2275             :     }
    2276             : 
    2277           0 :     if (!bHasReadSchema)
    2278           0 :         LoadSchema();
    2279             : 
    2280           0 :     if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
    2281           0 :         return OGRLayer::GetFeatureCount(bForce);
    2282             : 
    2283           0 :     return nTotalFeatureCount;
    2284             : }
    2285             : 
    2286             : /************************************************************************/
    2287             : /*                             GetDataset()                             */
    2288             : /************************************************************************/
    2289             : 
    2290          19 : GDALDataset *OGRGeoRSSLayer::GetDataset()
    2291             : {
    2292          19 :     return poDS;
    2293             : }

Generated by: LCOV version 1.14