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 1666 : static std::mutex &GetMutex()
41 : {
42 : static std::mutex goMutex;
43 1666 : 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 365 : OGRS101FeatureCatalog::GetSingletonFeatureCatalog(bool bStrict)
55 : {
56 365 : std::lock_guard lock(GetMutex());
57 365 : if (geStatus == LoadingStatus::UNINIT)
58 : {
59 : auto poFeatureCatalog =
60 56 : std::make_unique<OGRS101FeatureCatalog>(bStrict);
61 28 : geStatus = poFeatureCatalog->Load();
62 28 : if (geStatus == LoadingStatus::OK)
63 28 : gpoFeatureCatalog = poFeatureCatalog.release();
64 : }
65 730 : return {geStatus, gpoFeatureCatalog};
66 : }
67 :
68 : /************************************************************************/
69 : /* CleanupSingletonFeatureCatalog() */
70 : /************************************************************************/
71 :
72 : /** Clean the global feature catalog instance */
73 :
74 : /* static */
75 1301 : void OGRS101FeatureCatalog::CleanupSingletonFeatureCatalog()
76 : {
77 1301 : std::lock_guard lock(GetMutex());
78 1301 : geStatus = LoadingStatus::UNINIT;
79 : // Delete global object
80 1301 : CPL_IGNORE_RET_VAL(
81 2602 : std::unique_ptr<OGRS101FeatureCatalog>(gpoFeatureCatalog));
82 1301 : gpoFeatureCatalog = nullptr;
83 1301 : }
84 :
85 : /************************************************************************/
86 : /* OGRS101FeatureCatalog() */
87 : /************************************************************************/
88 :
89 : /** Constructor */
90 28 : OGRS101FeatureCatalog::OGRS101FeatureCatalog(bool bStrict) : m_bStrict(bStrict)
91 : {
92 28 : }
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 28 : OGRS101FeatureCatalog::LoadingStatus OGRS101FeatureCatalog::Load()
112 : {
113 28 : bool bError = false;
114 56 : const std::string osFilename = GetFilename(bError);
115 28 : if (bError)
116 0 : return LoadingStatus::ERROR;
117 28 : if (osFilename.empty())
118 0 : return LoadingStatus::SKIPPED;
119 :
120 28 : CPLDebugOnce("S101", "Reading feature catalog from %s", osFilename.c_str());
121 56 : CPLXMLTreeCloser oTree(CPLParseXMLFile(osFilename.c_str()));
122 28 : if (!oTree)
123 : {
124 0 : return m_bStrict ? LoadingStatus::ERROR : LoadingStatus::SKIPPED;
125 : }
126 28 : 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 28 : CPLStripXMLNamespace(oTree.get(), "S100FC", /* bRecurse = */ true);
135 28 : const auto psRoot = CPLGetXMLNode(oTree.get(), "=S100_FC_FeatureCatalogue");
136 28 : CPLAssert(psRoot);
137 :
138 28 : return LoadSimpleAttributes(osFilename.c_str(), psRoot) &&
139 28 : LoadComplexAttributes(osFilename.c_str(), psRoot) &&
140 28 : LoadInformationTypes(osFilename.c_str(), psRoot) &&
141 28 : LoadFeatureTypes(osFilename.c_str(), psRoot)
142 56 : ? LoadingStatus::OK
143 0 : : m_bStrict ? LoadingStatus::ERROR
144 28 : : LoadingStatus::SKIPPED;
145 : }
146 :
147 : /************************************************************************/
148 : /* GetFilename() */
149 : /************************************************************************/
150 :
151 : /** Get XML feature catalog filename. */
152 28 : std::string OGRS101FeatureCatalog::GetFilename(bool &bError) const
153 : {
154 28 : bError = false;
155 :
156 : // First try path given by GDAL_S101_FEATURE_CATALOG config option
157 28 : if (const char *pszFC =
158 28 : 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 56 : const std::string osCacheDir = GDALGetCacheDirectory();
176 28 : if (!osCacheDir.empty())
177 : {
178 : std::string osTmpFilename = CPLFormFilenameSafe(
179 28 : osCacheDir.c_str(), FEATURE_CATALOG_FILENAME, nullptr);
180 : VSIStatBufL sStat;
181 28 : if (VSIStatL(osTmpFilename.c_str(), &sStat) == 0)
182 : {
183 27 : 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 28 : bool OGRS101FeatureCatalog::LoadSimpleAttributes(const char *pszFC,
279 : const CPLXMLNode *psRoot)
280 : {
281 : const CPLXMLNode *psSimpleAttrs =
282 28 : CPLGetXMLNode(psRoot, "S100_FC_SimpleAttributes");
283 28 : if (!psSimpleAttrs)
284 : {
285 0 : return EMIT_ERROR_OR_WARNING(
286 : CPLSPrintf("Cannot find S100_FC_SimpleAttributes in %s", pszFC));
287 : }
288 6636 : for (const CPLXMLNode *psSimpleAttr = psSimpleAttrs->psChild; psSimpleAttr;
289 6608 : psSimpleAttr = psSimpleAttr->psNext)
290 : {
291 6608 : if (psSimpleAttr->eType == CXT_Element &&
292 6608 : strcmp(psSimpleAttr->pszValue, "S100_FC_SimpleAttribute") == 0)
293 : {
294 6608 : const char *pszCode = CPLGetXMLValue(psSimpleAttr, "code", nullptr);
295 : const char *pszValueType =
296 6608 : CPLGetXMLValue(psSimpleAttr, "valueType", nullptr);
297 6608 : if (pszCode && pszValueType)
298 : {
299 6608 : SimpleAttribute attr;
300 6608 : attr.code = pszCode;
301 6608 : attr.name = CPLGetXMLValue(psSimpleAttr, "name", "");
302 : attr.definition =
303 6608 : CPLGetXMLValue(psSimpleAttr, "definition", "");
304 6608 : attr.type = pszValueType;
305 :
306 6608 : constexpr const char *const apszValueTypes[] = {
307 : VALUE_TYPE_BOOLEAN,
308 : VALUE_TYPE_ENUMERATION,
309 : VALUE_TYPE_INTEGER,
310 : VALUE_TYPE_REAL,
311 : VALUE_TYPE_TRUNCATED_DATE,
312 : VALUE_TYPE_TEXT,
313 : VALUE_TYPE_TIME,
314 : VALUE_TYPE_URI,
315 : VALUE_TYPE_URN,
316 : };
317 6608 : if (std::find_if(std::begin(apszValueTypes),
318 : std::end(apszValueTypes),
319 19992 : [pszValueType](const char *x)
320 26600 : { return strcmp(x, pszValueType) == 0; }) ==
321 6608 : std::end(apszValueTypes))
322 : {
323 0 : if (!EMIT_ERROR_OR_WARNING(
324 : CPLSPrintf("In %s: unknown valueType %s in "
325 : "simple attribute of code %s",
326 : pszFC, pszValueType, pszCode)))
327 : {
328 0 : return false;
329 : }
330 : }
331 :
332 6608 : if (strcmp(pszValueType, "enumeration") == 0)
333 : {
334 3136 : if (const CPLXMLNode *psListedValues =
335 3136 : CPLGetXMLNode(psSimpleAttr, "listedValues"))
336 : {
337 3136 : for (const CPLXMLNode *psListedValue =
338 : psListedValues->psChild;
339 32088 : psListedValue;
340 28952 : psListedValue = psListedValue->psNext)
341 : {
342 28952 : if (psListedValue->eType == CXT_Element &&
343 28952 : strcmp(psListedValue->pszValue,
344 : "listedValue") == 0)
345 : {
346 28952 : const char *pszEnumLabel = CPLGetXMLValue(
347 28952 : psListedValue, "label", nullptr);
348 28952 : const char *pszEnumCode = CPLGetXMLValue(
349 : psListedValue, "code", nullptr);
350 57904 : if (pszEnumCode && pszEnumLabel &&
351 28952 : CPLGetValueType(pszEnumCode) ==
352 : CPL_VALUE_INTEGER)
353 : {
354 57904 : if (!attr.enumeratedValues
355 : .insert(
356 0 : std::pair<int, std::string>(
357 57904 : atoi(pszEnumCode),
358 57904 : std::move(pszEnumLabel)))
359 28952 : .second)
360 : {
361 0 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
362 : "%s: several listedValue with "
363 : "code "
364 : "%s in %s",
365 : pszFC, pszEnumCode, pszCode)))
366 : {
367 0 : return false;
368 : }
369 : }
370 : }
371 0 : else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
372 : "%s: invalid listedValue in "
373 : "simple attribute %s",
374 : pszFC, pszCode)))
375 : {
376 0 : return false;
377 : }
378 : }
379 : }
380 : }
381 : }
382 13216 : if (!m_simpleAttributes
383 13216 : .insert(std::pair<std::string, SimpleAttribute>(
384 13216 : pszCode, std::move(attr)))
385 6608 : .second)
386 : {
387 0 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
388 : "%s: several simple attributes with code %s", pszFC,
389 : pszCode)))
390 : {
391 0 : return false;
392 : }
393 6608 : }
394 : }
395 0 : else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
396 : "In %s: invalid S100_FC_SimpleAttribute", pszFC)))
397 : {
398 0 : return false;
399 : }
400 : }
401 : }
402 :
403 28 : return true;
404 : }
405 :
406 : /************************************************************************/
407 : /* LoadComplexAttributes() */
408 : /************************************************************************/
409 :
410 28 : bool OGRS101FeatureCatalog::LoadComplexAttributes(const char *pszFC,
411 : const CPLXMLNode *psRoot)
412 : {
413 : const CPLXMLNode *psComplexAttrs =
414 28 : CPLGetXMLNode(psRoot, "S100_FC_ComplexAttributes");
415 28 : if (!psComplexAttrs)
416 : {
417 0 : return EMIT_ERROR_OR_WARNING(
418 : CPLSPrintf("Cannot find S100_FC_ComplexAttributes in %s", pszFC));
419 : }
420 28 : for (const CPLXMLNode *psComplexAttr = psComplexAttrs->psChild;
421 1204 : psComplexAttr; psComplexAttr = psComplexAttr->psNext)
422 : {
423 1176 : if (psComplexAttr->eType == CXT_Element &&
424 1176 : strcmp(psComplexAttr->pszValue, "S100_FC_ComplexAttribute") == 0)
425 : {
426 : const char *pszCode =
427 1176 : CPLGetXMLValue(psComplexAttr, "code", nullptr);
428 1176 : if (pszCode)
429 : {
430 1176 : ComplexAttribute attr;
431 1176 : attr.code = pszCode;
432 1176 : attr.name = CPLGetXMLValue(psComplexAttr, "name", "");
433 : attr.definition =
434 1176 : CPLGetXMLValue(psComplexAttr, "definition", "");
435 :
436 : const CPLXMLNode *psSubAttributeBinding =
437 1176 : CPLGetXMLNode(psComplexAttr, "subAttributeBinding");
438 4396 : for (; psSubAttributeBinding;
439 3220 : psSubAttributeBinding = psSubAttributeBinding->psNext)
440 : {
441 3220 : if (psSubAttributeBinding->eType == CXT_Element &&
442 3220 : strcmp(psSubAttributeBinding->pszValue,
443 : "subAttributeBinding") == 0)
444 : {
445 3220 : const char *pszAttributeRef = CPLGetXMLValue(
446 : psSubAttributeBinding, "attribute.ref", nullptr);
447 3220 : if (pszAttributeRef)
448 : {
449 3220 : attr.attributeBindings.insert(pszAttributeRef);
450 : }
451 : }
452 : }
453 2352 : if (!m_complexAttributes
454 2352 : .insert(std::pair<std::string, ComplexAttribute>(
455 2352 : pszCode, std::move(attr)))
456 1176 : .second)
457 : {
458 0 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
459 : "In %s: several complex attributes with code %s",
460 : pszFC, pszCode)))
461 : {
462 0 : return false;
463 : }
464 : }
465 : }
466 0 : else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
467 : "In %s: invalid S100_FC_ComplexAttribute", pszFC)))
468 : {
469 0 : return false;
470 : }
471 : }
472 : }
473 :
474 1204 : for (const auto &[code, def] : m_complexAttributes)
475 : {
476 4396 : for (const auto &attrCode : def.attributeBindings)
477 : {
478 3864 : if (!cpl::contains(m_simpleAttributes, attrCode) &&
479 644 : !cpl::contains(m_complexAttributes, attrCode))
480 : {
481 0 : if (!EMIT_ERROR_OR_WARNING(
482 : CPLSPrintf("In %s: complex attribute %s refers to "
483 : "attribute %s which does not exist",
484 : pszFC, code.c_str(), attrCode.c_str())))
485 : {
486 0 : return false;
487 : }
488 : }
489 : }
490 : }
491 :
492 28 : return true;
493 : }
494 :
495 : /************************************************************************/
496 : /* LoadInformationTypes() */
497 : /************************************************************************/
498 :
499 28 : bool OGRS101FeatureCatalog::LoadInformationTypes(const char *pszFC,
500 : const CPLXMLNode *psRoot)
501 : {
502 : const CPLXMLNode *psInformationTypes =
503 28 : CPLGetXMLNode(psRoot, "S100_FC_InformationTypes");
504 28 : if (!psInformationTypes)
505 : {
506 0 : return EMIT_ERROR_OR_WARNING(
507 : CPLSPrintf("Cannot find S100_FC_InformationTypes in %s", pszFC));
508 : }
509 28 : for (const CPLXMLNode *psInformationType = psInformationTypes->psChild;
510 168 : psInformationType; psInformationType = psInformationType->psNext)
511 : {
512 140 : if (psInformationType->eType == CXT_Element &&
513 140 : strcmp(psInformationType->pszValue, "S100_FC_InformationType") == 0)
514 : {
515 : const char *pszCode =
516 140 : CPLGetXMLValue(psInformationType, "code", nullptr);
517 140 : if (pszCode)
518 : {
519 140 : InformationType featureType;
520 140 : featureType.code = pszCode;
521 : featureType.name =
522 140 : CPLGetXMLValue(psInformationType, "name", "");
523 : featureType.definition =
524 140 : CPLGetXMLValue(psInformationType, "definition", "");
525 : featureType.alias =
526 140 : CPLGetXMLValue(psInformationType, "alias", "");
527 :
528 : const CPLXMLNode *psAttributeBinding =
529 140 : CPLGetXMLNode(psInformationType, "attributeBinding");
530 784 : for (; psAttributeBinding;
531 644 : psAttributeBinding = psAttributeBinding->psNext)
532 : {
533 644 : if (psAttributeBinding->eType == CXT_Element &&
534 644 : strcmp(psAttributeBinding->pszValue,
535 : "attributeBinding") == 0)
536 : {
537 644 : const char *pszAttributeRef = CPLGetXMLValue(
538 644 : psAttributeBinding, "attribute.ref", nullptr);
539 644 : if (pszAttributeRef)
540 : {
541 644 : if (!cpl::contains(m_simpleAttributes,
542 1064 : pszAttributeRef) &&
543 420 : !cpl::contains(m_complexAttributes,
544 : pszAttributeRef))
545 : {
546 0 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
547 : "In %s: information type %s refers to "
548 : "attribute %s which does not exist",
549 : pszFC, pszCode, pszAttributeRef)))
550 : {
551 0 : return false;
552 : }
553 : }
554 :
555 : featureType.attributeBindings.insert(
556 644 : pszAttributeRef);
557 : }
558 : }
559 : }
560 :
561 280 : if (!m_informationTypes
562 280 : .insert(std::pair<std::string, InformationType>(
563 280 : pszCode, std::move(featureType)))
564 140 : .second)
565 : {
566 0 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
567 : "%s: several information types with code %s", pszFC,
568 : pszCode)))
569 : {
570 0 : return false;
571 : }
572 : }
573 : }
574 0 : else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
575 : "%s: invalid S100_FC_InformationType", pszFC)))
576 : {
577 0 : return false;
578 : }
579 : }
580 : }
581 :
582 28 : return true;
583 : }
584 :
585 : /************************************************************************/
586 : /* LoadFeatureTypes() */
587 : /************************************************************************/
588 :
589 28 : bool OGRS101FeatureCatalog::LoadFeatureTypes(const char *pszFC,
590 : const CPLXMLNode *psRoot)
591 : {
592 : const CPLXMLNode *psFeatureTypes =
593 28 : CPLGetXMLNode(psRoot, "S100_FC_FeatureTypes");
594 28 : if (!psFeatureTypes)
595 : {
596 0 : return EMIT_ERROR_OR_WARNING(
597 : CPLSPrintf("Cannot find S100_FC_FeatureTypes in %s", pszFC));
598 : }
599 28 : for (const CPLXMLNode *psFeatureType = psFeatureTypes->psChild;
600 5348 : psFeatureType; psFeatureType = psFeatureType->psNext)
601 : {
602 5320 : if (psFeatureType->eType == CXT_Element &&
603 5320 : strcmp(psFeatureType->pszValue, "S100_FC_FeatureType") == 0)
604 : {
605 : const char *pszCode =
606 5320 : CPLGetXMLValue(psFeatureType, "code", nullptr);
607 5320 : if (pszCode)
608 : {
609 5320 : FeatureType featureType;
610 5320 : featureType.code = pszCode;
611 5320 : featureType.name = CPLGetXMLValue(psFeatureType, "name", "");
612 : featureType.definition =
613 5320 : CPLGetXMLValue(psFeatureType, "definition", "");
614 5320 : featureType.alias = CPLGetXMLValue(psFeatureType, "alias", "");
615 :
616 : const CPLXMLNode *psAttributeBinding =
617 5320 : CPLGetXMLNode(psFeatureType, "attributeBinding");
618 95116 : for (; psAttributeBinding;
619 89796 : psAttributeBinding = psAttributeBinding->psNext)
620 : {
621 89796 : if (psAttributeBinding->eType == CXT_Element &&
622 89796 : strcmp(psAttributeBinding->pszValue,
623 : "attributeBinding") == 0)
624 : {
625 55944 : const char *pszAttributeRef = CPLGetXMLValue(
626 55944 : psAttributeBinding, "attribute.ref", nullptr);
627 55944 : if (pszAttributeRef)
628 : {
629 55944 : if (!cpl::contains(m_simpleAttributes,
630 73332 : pszAttributeRef) &&
631 17388 : !cpl::contains(m_complexAttributes,
632 : pszAttributeRef))
633 : {
634 0 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
635 : "In %s: feature type %s refers to "
636 : "attribute %s which does not exist",
637 : pszFC, pszCode, pszAttributeRef)))
638 : {
639 0 : return false;
640 : }
641 : }
642 :
643 : featureType.attributeBindings.insert(
644 55944 : pszAttributeRef);
645 : }
646 : }
647 : }
648 :
649 : const CPLXMLNode *psPermittedPrimitives =
650 5320 : CPLGetXMLNode(psFeatureType, "permittedPrimitives");
651 13916 : for (; psPermittedPrimitives;
652 8596 : psPermittedPrimitives = psPermittedPrimitives->psNext)
653 : {
654 8596 : if (psPermittedPrimitives->eType == CXT_Element &&
655 8596 : strcmp(psPermittedPrimitives->pszValue,
656 8596 : "permittedPrimitives") == 0 &&
657 8596 : psPermittedPrimitives->psChild &&
658 8596 : psPermittedPrimitives->psChild->eType == CXT_Text &&
659 8596 : psPermittedPrimitives->psChild->pszValue)
660 : {
661 8596 : const char *pszPermittedPrimitive =
662 8596 : psPermittedPrimitives->psChild->pszValue;
663 8596 : constexpr const char *const apszPermittedPrimitives[] =
664 : {
665 : PERMITTED_PRIMITIVE_NO_GEOMETRY,
666 : PERMITTED_PRIMITIVE_POINT,
667 : PERMITTED_PRIMITIVE_POINTSET,
668 : PERMITTED_PRIMITIVE_CURVE,
669 : PERMITTED_PRIMITIVE_SURFACE,
670 : };
671 8596 : if (std::find_if(
672 : std::begin(apszPermittedPrimitives),
673 : std::end(apszPermittedPrimitives),
674 31612 : [pszPermittedPrimitive](const char *x)
675 : {
676 31612 : return strcmp(x, pszPermittedPrimitive) ==
677 31612 : 0;
678 8596 : }) != std::end(apszPermittedPrimitives))
679 : {
680 : featureType.permittedPrimitives.insert(
681 8596 : pszPermittedPrimitive);
682 : }
683 0 : else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
684 : "In %s: unknown permittedPrimitives %s in "
685 : "feature type with code %s",
686 : pszFC, pszPermittedPrimitive, pszCode)))
687 : {
688 0 : return false;
689 : }
690 : }
691 : }
692 :
693 10640 : if (!m_featureTypes
694 10640 : .insert(std::pair<std::string, FeatureType>(
695 10640 : pszCode, std::move(featureType)))
696 5320 : .second)
697 : {
698 0 : if (!EMIT_ERROR_OR_WARNING(
699 : CPLSPrintf("%s: several feature types with code %s",
700 : pszFC, pszCode)))
701 : {
702 0 : return false;
703 : }
704 : }
705 : }
706 0 : else if (!EMIT_ERROR_OR_WARNING(
707 : CPLSPrintf("%s: invalid S100_FC_FeatureType", pszFC)))
708 : {
709 0 : return false;
710 : }
711 : }
712 : }
713 :
714 28 : return true;
715 : }
|