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