Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: S-101 driver
4 : * Purpose: Implements OGRS101FeatureCatalog
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2026, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "ogr_s101.h"
14 : #include "ogrs101featurecatalog.h"
15 : #include "cpl_http.h"
16 : #include "cpl_minixml.h"
17 : #include "cpl_multiproc.h"
18 :
19 : #include <algorithm>
20 : #include <cinttypes>
21 :
22 : #include <mutex>
23 :
24 : static OGRS101FeatureCatalog::LoadingStatus geStatus =
25 : OGRS101FeatureCatalog::LoadingStatus::UNINIT;
26 : static OGRS101FeatureCatalog *gpoFeatureCatalog = nullptr;
27 :
28 : // Matches S-101 2.0 spec
29 : constexpr const char *FEATURE_CATALOG_URL =
30 : "https://raw.githubusercontent.com/iho-ohi/S-101-Documentation-and-FC/"
31 : "fbcf8aaa72331a33668aa554e702e2a28f27cbc0/S-101FC/FeatureCatalogue.xml";
32 : constexpr const char *FEATURE_CATALOG_FILENAME =
33 : "101_Feature_Catalogue_2.0.0.xml";
34 : constexpr int FEATURE_CATALOG_EXPECTED_FILE_SIZE = 2017402;
35 :
36 : /************************************************************************/
37 : /* GetMutex() */
38 : /************************************************************************/
39 :
40 1529 : static std::mutex &GetMutex()
41 : {
42 : static std::mutex goMutex;
43 1529 : return goMutex;
44 : }
45 :
46 : /************************************************************************/
47 : /* GetSingletonFeatureCatalog() */
48 : /************************************************************************/
49 :
50 : /** Get the global feature catalog instance */
51 :
52 : /* static */
53 : std::pair<OGRS101FeatureCatalog::LoadingStatus, const OGRS101FeatureCatalog *>
54 266 : OGRS101FeatureCatalog::GetSingletonFeatureCatalog(bool bStrict)
55 : {
56 266 : std::lock_guard lock(GetMutex());
57 266 : if (geStatus == LoadingStatus::UNINIT)
58 : {
59 : auto poFeatureCatalog =
60 28 : std::make_unique<OGRS101FeatureCatalog>(bStrict);
61 14 : geStatus = poFeatureCatalog->Load();
62 14 : if (geStatus == LoadingStatus::OK)
63 14 : gpoFeatureCatalog = poFeatureCatalog.release();
64 : }
65 532 : return {geStatus, gpoFeatureCatalog};
66 : }
67 :
68 : /************************************************************************/
69 : /* CleanupSingletonFeatureCatalog() */
70 : /************************************************************************/
71 :
72 : /** Clean the global feature catalog instance */
73 :
74 : /* static */
75 1263 : void OGRS101FeatureCatalog::CleanupSingletonFeatureCatalog()
76 : {
77 1263 : std::lock_guard lock(GetMutex());
78 1263 : geStatus = LoadingStatus::UNINIT;
79 : // Delete global object
80 1263 : CPL_IGNORE_RET_VAL(
81 2526 : std::unique_ptr<OGRS101FeatureCatalog>(gpoFeatureCatalog));
82 1263 : gpoFeatureCatalog = nullptr;
83 1263 : }
84 :
85 : /************************************************************************/
86 : /* OGRS101FeatureCatalog() */
87 : /************************************************************************/
88 :
89 : /** Constructor */
90 14 : OGRS101FeatureCatalog::OGRS101FeatureCatalog(bool bStrict) : m_bStrict(bStrict)
91 : {
92 14 : }
93 :
94 : /************************************************************************/
95 : /* EmitErrorOrWarning() */
96 : /************************************************************************/
97 :
98 0 : /*static*/ bool OGRS101FeatureCatalog::EmitErrorOrWarning(
99 : const char *pszFile, const char *pszFunc, int nLine, const char *pszMsg,
100 : bool bError, bool bRecoverable)
101 : {
102 0 : return OGRS101Reader::EmitErrorOrWarning(pszFile, pszFunc, nLine, pszMsg,
103 0 : bError, bRecoverable);
104 : }
105 :
106 : /************************************************************************/
107 : /* Load() */
108 : /************************************************************************/
109 :
110 : /** Load the feature catalog XML file. */
111 14 : OGRS101FeatureCatalog::LoadingStatus OGRS101FeatureCatalog::Load()
112 : {
113 14 : bool bError = false;
114 28 : const std::string osFilename = GetFilename(bError);
115 14 : if (bError)
116 0 : return LoadingStatus::ERROR;
117 14 : if (osFilename.empty())
118 0 : return LoadingStatus::SKIPPED;
119 :
120 14 : CPLDebugOnce("S101", "Reading feature catalog from %s", osFilename.c_str());
121 28 : CPLXMLTreeCloser oTree(CPLParseXMLFile(osFilename.c_str()));
122 14 : if (!oTree)
123 : {
124 0 : return m_bStrict ? LoadingStatus::ERROR : LoadingStatus::SKIPPED;
125 : }
126 14 : if (!CPLGetXMLNode(oTree.get(), "=S100FC:S100_FC_FeatureCatalogue"))
127 : {
128 0 : return EMIT_ERROR_OR_WARNING(CPLSPrintf(
129 : "Cannot find S100FC:S100_FC_FeatureCatalogue in %s",
130 : osFilename.c_str()))
131 0 : ? LoadingStatus::SKIPPED
132 0 : : LoadingStatus::ERROR;
133 : }
134 14 : CPLStripXMLNamespace(oTree.get(), "S100FC", /* bRecurse = */ true);
135 14 : const auto psRoot = CPLGetXMLNode(oTree.get(), "=S100_FC_FeatureCatalogue");
136 14 : CPLAssert(psRoot);
137 :
138 14 : return LoadSimpleAttributes(osFilename.c_str(), psRoot) &&
139 14 : LoadComplexAttributes(osFilename.c_str(), psRoot) &&
140 14 : LoadInformationTypes(osFilename.c_str(), psRoot) &&
141 14 : LoadFeatureTypes(osFilename.c_str(), psRoot)
142 28 : ? LoadingStatus::OK
143 0 : : m_bStrict ? LoadingStatus::ERROR
144 14 : : LoadingStatus::SKIPPED;
145 : }
146 :
147 : /************************************************************************/
148 : /* GetFilename() */
149 : /************************************************************************/
150 :
151 : /** Get XML feature catalog filename. */
152 14 : std::string OGRS101FeatureCatalog::GetFilename(bool &bError) const
153 : {
154 14 : bError = false;
155 :
156 : // First try path given by GDAL_S101_FEATURE_CATALOG config option
157 14 : if (const char *pszFC =
158 14 : CPLGetConfigOption("GDAL_S101_FEATURE_CATALOG", nullptr))
159 : {
160 0 : if (EQUAL(pszFC, "") || EQUAL(pszFC, "NO") || EQUAL(pszFC, "FALSE") ||
161 0 : EQUAL(pszFC, "OFF") || EQUAL(pszFC, "0"))
162 0 : return {};
163 :
164 : VSIStatBufL sStat;
165 0 : if (VSIStatL(pszFC, &sStat) != 0)
166 : {
167 0 : bError = !EMIT_ERROR_OR_WARNING(
168 : CPLSPrintf("Feature catalog %s cannot be found", pszFC));
169 0 : return {};
170 : }
171 0 : return pszFC;
172 : }
173 :
174 : // Then try file cached in ~/.gdal directory
175 28 : const std::string osCacheDir = GDALGetCacheDirectory();
176 14 : if (!osCacheDir.empty())
177 : {
178 : std::string osTmpFilename = CPLFormFilenameSafe(
179 14 : osCacheDir.c_str(), FEATURE_CATALOG_FILENAME, nullptr);
180 : VSIStatBufL sStat;
181 14 : if (VSIStatL(osTmpFilename.c_str(), &sStat) == 0)
182 : {
183 13 : return osTmpFilename;
184 : }
185 : }
186 :
187 : // Then try file in ${prefix}/share/gdal/
188 : {
189 1 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
190 1 : if (const char *pszFC = CPLFindFile("gdal", FEATURE_CATALOG_FILENAME))
191 : {
192 0 : return pszFC;
193 : }
194 : }
195 :
196 : // Finally try do download file from the web
197 1 : if (!osCacheDir.empty() && CPLHTTPEnabled())
198 : {
199 : // Try to download catalog from FEATURE_CATALOG_URL only
200 : // once per process
201 3 : static const std::string osStaticString = [&osCacheDir]()
202 : {
203 1 : std::string osFilenameLocal;
204 : CPLHTTPResult *psResult;
205 : {
206 1 : CPLDebug("S101", "Downloading feature catalog from %s",
207 : FEATURE_CATALOG_URL);
208 1 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
209 1 : const char *const apszOptions[] = {"TIMEOUT=1", nullptr};
210 1 : psResult = CPLHTTPFetch(FEATURE_CATALOG_URL, apszOptions);
211 : }
212 1 : if (psResult)
213 : {
214 1 : if (psResult->nStatus == 0 &&
215 1 : psResult->nDataLen == FEATURE_CATALOG_EXPECTED_FILE_SIZE)
216 : {
217 : VSIStatBufL sStat;
218 1 : if (VSIStatL(osCacheDir.c_str(), &sStat) != 0)
219 0 : VSIMkdir(osCacheDir.c_str(), 0755);
220 :
221 : std::string osFilename = CPLFormFilenameSafe(
222 2 : osCacheDir.c_str(), FEATURE_CATALOG_FILENAME, nullptr);
223 : const std::string osFilenameTmp =
224 0 : osFilename + ".tmp" +
225 : CPLSPrintf("_%" PRId64,
226 2 : static_cast<int64_t>(CPLGetPID()));
227 1 : VSILFILE *f = VSIFOpenL(osFilenameTmp.c_str(), "wb");
228 1 : if (f)
229 : {
230 2 : bool bOK = VSIFWriteL(psResult->pabyData,
231 1 : psResult->nDataLen, 1, f) == 1;
232 1 : bOK = VSIFCloseL(f) == 0 && bOK;
233 1 : bOK = bOK && VSIRename(osFilenameTmp.c_str(),
234 : osFilename.c_str()) == 0;
235 1 : if (bOK)
236 : {
237 1 : osFilenameLocal = std::move(osFilename);
238 1 : CPLDebug("S101",
239 : "Feature catalog successfully "
240 : "written in %s",
241 : osFilenameLocal.c_str());
242 : }
243 : else
244 : {
245 0 : VSIUnlink(osFilenameTmp.c_str());
246 : }
247 : }
248 : else
249 : {
250 0 : CPLDebug("S101", "Cannot write temporary file %s",
251 : osFilenameTmp.c_str());
252 1 : }
253 : }
254 : else
255 : {
256 0 : CPLDebug(
257 : "S101",
258 : "Downloading of %s failed (status=%d, filesize=%d)",
259 : FEATURE_CATALOG_URL, psResult->nStatus,
260 : psResult->nDataLen);
261 : }
262 1 : CPLHTTPDestroyResult(psResult);
263 : }
264 1 : return osFilenameLocal;
265 1 : }();
266 :
267 1 : return osStaticString;
268 : }
269 :
270 0 : CPLDebugOnce("S101", "Cannot find feature catalog XML");
271 0 : return {};
272 : }
273 :
274 : /************************************************************************/
275 : /* LoadSimpleAttributes() */
276 : /************************************************************************/
277 :
278 14 : bool OGRS101FeatureCatalog::LoadSimpleAttributes(const char *pszFC,
279 : const CPLXMLNode *psRoot)
280 : {
281 : const CPLXMLNode *psSimpleAttrs =
282 14 : CPLGetXMLNode(psRoot, "S100_FC_SimpleAttributes");
283 14 : if (!psSimpleAttrs)
284 : {
285 0 : return EMIT_ERROR_OR_WARNING(
286 : CPLSPrintf("Cannot find S100_FC_SimpleAttributes in %s", pszFC));
287 : }
288 3318 : for (const CPLXMLNode *psSimpleAttr = psSimpleAttrs->psChild; psSimpleAttr;
289 3304 : psSimpleAttr = psSimpleAttr->psNext)
290 : {
291 3304 : if (psSimpleAttr->eType == CXT_Element &&
292 3304 : strcmp(psSimpleAttr->pszValue, "S100_FC_SimpleAttribute") == 0)
293 : {
294 3304 : const char *pszCode = CPLGetXMLValue(psSimpleAttr, "code", nullptr);
295 : const char *pszValueType =
296 3304 : CPLGetXMLValue(psSimpleAttr, "valueType", nullptr);
297 3304 : if (pszCode && pszValueType)
298 : {
299 3304 : SimpleAttribute attr;
300 3304 : attr.code = pszCode;
301 3304 : attr.name = CPLGetXMLValue(psSimpleAttr, "name", "");
302 3304 : attr.type = pszValueType;
303 :
304 3304 : constexpr const char *const apszValueTypes[] = {
305 : VALUE_TYPE_BOOLEAN,
306 : VALUE_TYPE_ENUMERATION,
307 : VALUE_TYPE_INTEGER,
308 : VALUE_TYPE_REAL,
309 : VALUE_TYPE_TRUNCATED_DATE,
310 : VALUE_TYPE_TEXT,
311 : VALUE_TYPE_TIME,
312 : VALUE_TYPE_URI,
313 : VALUE_TYPE_URN,
314 : };
315 3304 : if (std::find_if(std::begin(apszValueTypes),
316 : std::end(apszValueTypes),
317 9996 : [pszValueType](const char *x)
318 13300 : { return strcmp(x, pszValueType) == 0; }) ==
319 3304 : std::end(apszValueTypes))
320 : {
321 0 : if (!EMIT_ERROR_OR_WARNING(
322 : CPLSPrintf("In %s: unknown valueType %s in "
323 : "simple attribute of code %s",
324 : pszFC, pszValueType, pszCode)))
325 : {
326 0 : return false;
327 : }
328 : }
329 :
330 3304 : if (strcmp(pszValueType, "enumeration") == 0)
331 : {
332 1568 : if (const CPLXMLNode *psListedValues =
333 1568 : CPLGetXMLNode(psSimpleAttr, "listedValues"))
334 : {
335 1568 : for (const CPLXMLNode *psListedValue =
336 : psListedValues->psChild;
337 16044 : psListedValue;
338 14476 : psListedValue = psListedValue->psNext)
339 : {
340 14476 : if (psListedValue->eType == CXT_Element &&
341 14476 : strcmp(psListedValue->pszValue,
342 : "listedValue") == 0)
343 : {
344 14476 : const char *pszEnumLabel = CPLGetXMLValue(
345 14476 : psListedValue, "label", nullptr);
346 14476 : const char *pszEnumCode = CPLGetXMLValue(
347 : psListedValue, "code", nullptr);
348 28952 : if (pszEnumCode && pszEnumLabel &&
349 14476 : CPLGetValueType(pszEnumCode) ==
350 : CPL_VALUE_INTEGER)
351 : {
352 28952 : if (!attr.enumeratedValues
353 : .insert(
354 0 : std::pair<int, std::string>(
355 28952 : atoi(pszEnumCode),
356 28952 : std::move(pszEnumLabel)))
357 14476 : .second)
358 : {
359 0 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
360 : "%s: several listedValue with "
361 : "code "
362 : "%s in %s",
363 : pszFC, pszEnumCode, pszCode)))
364 : {
365 0 : return false;
366 : }
367 : }
368 : }
369 0 : else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
370 : "%s: invalid listedValue in "
371 : "simple attribute %s",
372 : pszFC, pszCode)))
373 : {
374 0 : return false;
375 : }
376 : }
377 : }
378 : }
379 : }
380 6608 : if (!m_simpleAttributes
381 6608 : .insert(std::pair<std::string, SimpleAttribute>(
382 6608 : pszCode, std::move(attr)))
383 3304 : .second)
384 : {
385 0 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
386 : "%s: several simple attributes with code %s", pszFC,
387 : pszCode)))
388 : {
389 0 : return false;
390 : }
391 3304 : }
392 : }
393 0 : else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
394 : "In %s: invalid S100_FC_SimpleAttribute", pszFC)))
395 : {
396 0 : return false;
397 : }
398 : }
399 : }
400 :
401 14 : return true;
402 : }
403 :
404 : /************************************************************************/
405 : /* LoadComplexAttributes() */
406 : /************************************************************************/
407 :
408 14 : bool OGRS101FeatureCatalog::LoadComplexAttributes(const char *pszFC,
409 : const CPLXMLNode *psRoot)
410 : {
411 : const CPLXMLNode *psComplexAttrs =
412 14 : CPLGetXMLNode(psRoot, "S100_FC_ComplexAttributes");
413 14 : if (!psComplexAttrs)
414 : {
415 0 : return EMIT_ERROR_OR_WARNING(
416 : CPLSPrintf("Cannot find S100_FC_ComplexAttributes in %s", pszFC));
417 : }
418 14 : for (const CPLXMLNode *psComplexAttr = psComplexAttrs->psChild;
419 602 : psComplexAttr; psComplexAttr = psComplexAttr->psNext)
420 : {
421 588 : if (psComplexAttr->eType == CXT_Element &&
422 588 : strcmp(psComplexAttr->pszValue, "S100_FC_ComplexAttribute") == 0)
423 : {
424 : const char *pszCode =
425 588 : CPLGetXMLValue(psComplexAttr, "code", nullptr);
426 588 : if (pszCode)
427 : {
428 588 : ComplexAttribute attr;
429 588 : attr.code = pszCode;
430 588 : attr.name = CPLGetXMLValue(psComplexAttr, "name", "");
431 :
432 : const CPLXMLNode *psSubAttributeBinding =
433 588 : CPLGetXMLNode(psComplexAttr, "subAttributeBinding");
434 2198 : for (; psSubAttributeBinding;
435 1610 : psSubAttributeBinding = psSubAttributeBinding->psNext)
436 : {
437 1610 : if (psSubAttributeBinding->eType == CXT_Element &&
438 1610 : strcmp(psSubAttributeBinding->pszValue,
439 : "subAttributeBinding") == 0)
440 : {
441 1610 : const char *pszAttributeRef = CPLGetXMLValue(
442 : psSubAttributeBinding, "attribute.ref", nullptr);
443 1610 : if (pszAttributeRef)
444 : {
445 1610 : attr.attributeBindings.insert(pszAttributeRef);
446 : }
447 : }
448 : }
449 1176 : if (!m_complexAttributes
450 1176 : .insert(std::pair<std::string, ComplexAttribute>(
451 1176 : pszCode, std::move(attr)))
452 588 : .second)
453 : {
454 0 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
455 : "In %s: several complex attributes with code %s",
456 : pszFC, pszCode)))
457 : {
458 0 : return false;
459 : }
460 : }
461 : }
462 0 : else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
463 : "In %s: invalid S100_FC_ComplexAttribute", pszFC)))
464 : {
465 0 : return false;
466 : }
467 : }
468 : }
469 :
470 602 : for (const auto &[code, def] : m_complexAttributes)
471 : {
472 2198 : for (const auto &attrCode : def.attributeBindings)
473 : {
474 1932 : if (!cpl::contains(m_simpleAttributes, attrCode) &&
475 322 : !cpl::contains(m_complexAttributes, attrCode))
476 : {
477 0 : if (!EMIT_ERROR_OR_WARNING(
478 : CPLSPrintf("In %s: complex attribute %s refers to "
479 : "attribute %s which does not exist",
480 : pszFC, code.c_str(), attrCode.c_str())))
481 : {
482 0 : return false;
483 : }
484 : }
485 : }
486 : }
487 :
488 14 : return true;
489 : }
490 :
491 : /************************************************************************/
492 : /* LoadInformationTypes() */
493 : /************************************************************************/
494 :
495 14 : bool OGRS101FeatureCatalog::LoadInformationTypes(const char *pszFC,
496 : const CPLXMLNode *psRoot)
497 : {
498 : const CPLXMLNode *psInformationTypes =
499 14 : CPLGetXMLNode(psRoot, "S100_FC_InformationTypes");
500 14 : if (!psInformationTypes)
501 : {
502 0 : return EMIT_ERROR_OR_WARNING(
503 : CPLSPrintf("Cannot find S100_FC_InformationTypes in %s", pszFC));
504 : }
505 14 : for (const CPLXMLNode *psInformationType = psInformationTypes->psChild;
506 84 : psInformationType; psInformationType = psInformationType->psNext)
507 : {
508 70 : if (psInformationType->eType == CXT_Element &&
509 70 : strcmp(psInformationType->pszValue, "S100_FC_InformationType") == 0)
510 : {
511 : const char *pszCode =
512 70 : CPLGetXMLValue(psInformationType, "code", nullptr);
513 70 : if (pszCode)
514 : {
515 70 : InformationType featureType;
516 70 : featureType.code = pszCode;
517 : featureType.name =
518 70 : CPLGetXMLValue(psInformationType, "name", "");
519 : featureType.definition =
520 70 : CPLGetXMLValue(psInformationType, "definition", "");
521 : featureType.alias =
522 70 : CPLGetXMLValue(psInformationType, "alias", "");
523 :
524 : const CPLXMLNode *psAttributeBinding =
525 70 : CPLGetXMLNode(psInformationType, "attributeBinding");
526 392 : for (; psAttributeBinding;
527 322 : psAttributeBinding = psAttributeBinding->psNext)
528 : {
529 322 : if (psAttributeBinding->eType == CXT_Element &&
530 322 : strcmp(psAttributeBinding->pszValue,
531 : "attributeBinding") == 0)
532 : {
533 322 : const char *pszAttributeRef = CPLGetXMLValue(
534 322 : psAttributeBinding, "attribute.ref", nullptr);
535 322 : if (pszAttributeRef)
536 : {
537 322 : if (!cpl::contains(m_simpleAttributes,
538 532 : pszAttributeRef) &&
539 210 : !cpl::contains(m_complexAttributes,
540 : pszAttributeRef))
541 : {
542 0 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
543 : "In %s: information type %s refers to "
544 : "attribute %s which does not exist",
545 : pszFC, pszCode, pszAttributeRef)))
546 : {
547 0 : return false;
548 : }
549 : }
550 :
551 : featureType.attributeBindings.insert(
552 322 : pszAttributeRef);
553 : }
554 : }
555 : }
556 :
557 140 : if (!m_informationTypes
558 140 : .insert(std::pair<std::string, InformationType>(
559 140 : pszCode, std::move(featureType)))
560 70 : .second)
561 : {
562 0 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
563 : "%s: several information types with code %s", pszFC,
564 : pszCode)))
565 : {
566 0 : return false;
567 : }
568 : }
569 : }
570 0 : else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
571 : "%s: invalid S100_FC_InformationType", pszFC)))
572 : {
573 0 : return false;
574 : }
575 : }
576 : }
577 :
578 14 : return true;
579 : }
580 :
581 : /************************************************************************/
582 : /* LoadFeatureTypes() */
583 : /************************************************************************/
584 :
585 14 : bool OGRS101FeatureCatalog::LoadFeatureTypes(const char *pszFC,
586 : const CPLXMLNode *psRoot)
587 : {
588 : const CPLXMLNode *psFeatureTypes =
589 14 : CPLGetXMLNode(psRoot, "S100_FC_FeatureTypes");
590 14 : if (!psFeatureTypes)
591 : {
592 0 : return EMIT_ERROR_OR_WARNING(
593 : CPLSPrintf("Cannot find S100_FC_FeatureTypes in %s", pszFC));
594 : }
595 14 : for (const CPLXMLNode *psFeatureType = psFeatureTypes->psChild;
596 2674 : psFeatureType; psFeatureType = psFeatureType->psNext)
597 : {
598 2660 : if (psFeatureType->eType == CXT_Element &&
599 2660 : strcmp(psFeatureType->pszValue, "S100_FC_FeatureType") == 0)
600 : {
601 : const char *pszCode =
602 2660 : CPLGetXMLValue(psFeatureType, "code", nullptr);
603 2660 : if (pszCode)
604 : {
605 2660 : FeatureType featureType;
606 2660 : featureType.code = pszCode;
607 2660 : featureType.name = CPLGetXMLValue(psFeatureType, "name", "");
608 : featureType.definition =
609 2660 : CPLGetXMLValue(psFeatureType, "definition", "");
610 2660 : featureType.alias = CPLGetXMLValue(psFeatureType, "alias", "");
611 :
612 : const CPLXMLNode *psAttributeBinding =
613 2660 : CPLGetXMLNode(psFeatureType, "attributeBinding");
614 47558 : for (; psAttributeBinding;
615 44898 : psAttributeBinding = psAttributeBinding->psNext)
616 : {
617 44898 : if (psAttributeBinding->eType == CXT_Element &&
618 44898 : strcmp(psAttributeBinding->pszValue,
619 : "attributeBinding") == 0)
620 : {
621 27972 : const char *pszAttributeRef = CPLGetXMLValue(
622 27972 : psAttributeBinding, "attribute.ref", nullptr);
623 27972 : if (pszAttributeRef)
624 : {
625 27972 : if (!cpl::contains(m_simpleAttributes,
626 36666 : pszAttributeRef) &&
627 8694 : !cpl::contains(m_complexAttributes,
628 : pszAttributeRef))
629 : {
630 0 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
631 : "In %s: feature type %s refers to "
632 : "attribute %s which does not exist",
633 : pszFC, pszCode, pszAttributeRef)))
634 : {
635 0 : return false;
636 : }
637 : }
638 :
639 : featureType.attributeBindings.insert(
640 27972 : pszAttributeRef);
641 : }
642 : }
643 : }
644 :
645 : const CPLXMLNode *psPermittedPrimitives =
646 2660 : CPLGetXMLNode(psFeatureType, "permittedPrimitives");
647 6958 : for (; psPermittedPrimitives;
648 4298 : psPermittedPrimitives = psPermittedPrimitives->psNext)
649 : {
650 4298 : if (psPermittedPrimitives->eType == CXT_Element &&
651 4298 : strcmp(psPermittedPrimitives->pszValue,
652 4298 : "permittedPrimitives") == 0 &&
653 4298 : psPermittedPrimitives->psChild &&
654 4298 : psPermittedPrimitives->psChild->eType == CXT_Text &&
655 4298 : psPermittedPrimitives->psChild->pszValue)
656 : {
657 4298 : const char *pszPermittedPrimitive =
658 4298 : psPermittedPrimitives->psChild->pszValue;
659 4298 : constexpr const char *const apszPermittedPrimitives[] =
660 : {
661 : PERMITTED_PRIMITIVE_NO_GEOMETRY,
662 : PERMITTED_PRIMITIVE_POINT,
663 : PERMITTED_PRIMITIVE_POINTSET,
664 : PERMITTED_PRIMITIVE_CURVE,
665 : PERMITTED_PRIMITIVE_SURFACE,
666 : };
667 4298 : if (std::find_if(
668 : std::begin(apszPermittedPrimitives),
669 : std::end(apszPermittedPrimitives),
670 15806 : [pszPermittedPrimitive](const char *x)
671 : {
672 15806 : return strcmp(x, pszPermittedPrimitive) ==
673 15806 : 0;
674 4298 : }) != std::end(apszPermittedPrimitives))
675 : {
676 : featureType.permittedPrimitives.insert(
677 4298 : pszPermittedPrimitive);
678 : }
679 0 : else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
680 : "In %s: unknown permittedPrimitives %s in "
681 : "feature type with code %s",
682 : pszFC, pszPermittedPrimitive, pszCode)))
683 : {
684 0 : return false;
685 : }
686 : }
687 : }
688 :
689 5320 : if (!m_featureTypes
690 5320 : .insert(std::pair<std::string, FeatureType>(
691 5320 : pszCode, std::move(featureType)))
692 2660 : .second)
693 : {
694 0 : if (!EMIT_ERROR_OR_WARNING(
695 : CPLSPrintf("%s: several feature types with code %s",
696 : pszFC, pszCode)))
697 : {
698 0 : return false;
699 : }
700 : }
701 : }
702 0 : else if (!EMIT_ERROR_OR_WARNING(
703 : CPLSPrintf("%s: invalid S100_FC_FeatureType", pszFC)))
704 : {
705 0 : return false;
706 : }
707 : }
708 : }
709 :
710 14 : return true;
711 : }
|