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

Generated by: LCOV version 1.14