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 1656 : static std::mutex &GetMutex()
41 : {
42 : static std::mutex goMutex;
43 1656 : 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 362 : OGRS101FeatureCatalog::GetSingletonFeatureCatalog(bool bStrict)
55 : {
56 362 : std::lock_guard lock(GetMutex());
57 362 : if (geStatus == LoadingStatus::UNINIT)
58 : {
59 : auto poFeatureCatalog =
60 54 : std::make_unique<OGRS101FeatureCatalog>(bStrict);
61 27 : geStatus = poFeatureCatalog->Load();
62 27 : if (geStatus == LoadingStatus::OK)
63 27 : gpoFeatureCatalog = poFeatureCatalog.release();
64 : }
65 724 : return {geStatus, gpoFeatureCatalog};
66 : }
67 :
68 : /************************************************************************/
69 : /* CleanupSingletonFeatureCatalog() */
70 : /************************************************************************/
71 :
72 : /** Clean the global feature catalog instance */
73 :
74 : /* static */
75 1294 : void OGRS101FeatureCatalog::CleanupSingletonFeatureCatalog()
76 : {
77 1294 : std::lock_guard lock(GetMutex());
78 1294 : geStatus = LoadingStatus::UNINIT;
79 : // Delete global object
80 1294 : CPL_IGNORE_RET_VAL(
81 2588 : std::unique_ptr<OGRS101FeatureCatalog>(gpoFeatureCatalog));
82 1294 : gpoFeatureCatalog = nullptr;
83 1294 : }
84 :
85 : /************************************************************************/
86 : /* OGRS101FeatureCatalog() */
87 : /************************************************************************/
88 :
89 : /** Constructor */
90 27 : OGRS101FeatureCatalog::OGRS101FeatureCatalog(bool bStrict) : m_bStrict(bStrict)
91 : {
92 27 : }
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 27 : OGRS101FeatureCatalog::LoadingStatus OGRS101FeatureCatalog::Load()
112 : {
113 27 : bool bError = false;
114 54 : const std::string osFilename = GetFilename(bError);
115 27 : if (bError)
116 0 : return LoadingStatus::ERROR;
117 27 : if (osFilename.empty())
118 0 : return LoadingStatus::SKIPPED;
119 :
120 27 : CPLDebugOnce("S101", "Reading feature catalog from %s", osFilename.c_str());
121 54 : CPLXMLTreeCloser oTree(CPLParseXMLFile(osFilename.c_str()));
122 27 : if (!oTree)
123 : {
124 0 : return m_bStrict ? LoadingStatus::ERROR : LoadingStatus::SKIPPED;
125 : }
126 27 : 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 27 : CPLStripXMLNamespace(oTree.get(), "S100FC", /* bRecurse = */ true);
135 27 : const auto psRoot = CPLGetXMLNode(oTree.get(), "=S100_FC_FeatureCatalogue");
136 27 : CPLAssert(psRoot);
137 :
138 27 : return LoadSimpleAttributes(osFilename.c_str(), psRoot) &&
139 27 : LoadComplexAttributes(osFilename.c_str(), psRoot) &&
140 27 : LoadInformationTypes(osFilename.c_str(), psRoot) &&
141 27 : LoadFeatureTypes(osFilename.c_str(), psRoot)
142 54 : ? LoadingStatus::OK
143 0 : : m_bStrict ? LoadingStatus::ERROR
144 27 : : LoadingStatus::SKIPPED;
145 : }
146 :
147 : /************************************************************************/
148 : /* GetFilename() */
149 : /************************************************************************/
150 :
151 : /** Get XML feature catalog filename. */
152 27 : std::string OGRS101FeatureCatalog::GetFilename(bool &bError) const
153 : {
154 27 : bError = false;
155 :
156 : // First try path given by GDAL_S101_FEATURE_CATALOG config option
157 27 : if (const char *pszFC =
158 27 : 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 54 : const std::string osCacheDir = GDALGetCacheDirectory();
176 27 : if (!osCacheDir.empty())
177 : {
178 : std::string osTmpFilename = CPLFormFilenameSafe(
179 27 : osCacheDir.c_str(), FEATURE_CATALOG_FILENAME, nullptr);
180 : VSIStatBufL sStat;
181 27 : if (VSIStatL(osTmpFilename.c_str(), &sStat) == 0)
182 : {
183 26 : 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 27 : bool OGRS101FeatureCatalog::LoadSimpleAttributes(const char *pszFC,
279 : const CPLXMLNode *psRoot)
280 : {
281 : const CPLXMLNode *psSimpleAttrs =
282 27 : CPLGetXMLNode(psRoot, "S100_FC_SimpleAttributes");
283 27 : if (!psSimpleAttrs)
284 : {
285 0 : return EMIT_ERROR_OR_WARNING(
286 : CPLSPrintf("Cannot find S100_FC_SimpleAttributes in %s", pszFC));
287 : }
288 6399 : for (const CPLXMLNode *psSimpleAttr = psSimpleAttrs->psChild; psSimpleAttr;
289 6372 : psSimpleAttr = psSimpleAttr->psNext)
290 : {
291 6372 : if (psSimpleAttr->eType == CXT_Element &&
292 6372 : strcmp(psSimpleAttr->pszValue, "S100_FC_SimpleAttribute") == 0)
293 : {
294 6372 : const char *pszCode = CPLGetXMLValue(psSimpleAttr, "code", nullptr);
295 : const char *pszValueType =
296 6372 : CPLGetXMLValue(psSimpleAttr, "valueType", nullptr);
297 6372 : if (pszCode && pszValueType)
298 : {
299 6372 : SimpleAttribute attr;
300 6372 : attr.code = pszCode;
301 6372 : attr.name = CPLGetXMLValue(psSimpleAttr, "name", "");
302 : attr.definition =
303 6372 : CPLGetXMLValue(psSimpleAttr, "definition", "");
304 6372 : attr.type = pszValueType;
305 :
306 6372 : 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 6372 : if (std::find_if(std::begin(apszValueTypes),
318 : std::end(apszValueTypes),
319 19278 : [pszValueType](const char *x)
320 25650 : { return strcmp(x, pszValueType) == 0; }) ==
321 6372 : 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 6372 : if (strcmp(pszValueType, "enumeration") == 0)
333 : {
334 3024 : if (const CPLXMLNode *psListedValues =
335 3024 : CPLGetXMLNode(psSimpleAttr, "listedValues"))
336 : {
337 3024 : for (const CPLXMLNode *psListedValue =
338 : psListedValues->psChild;
339 30942 : psListedValue;
340 27918 : psListedValue = psListedValue->psNext)
341 : {
342 27918 : if (psListedValue->eType == CXT_Element &&
343 27918 : strcmp(psListedValue->pszValue,
344 : "listedValue") == 0)
345 : {
346 27918 : const char *pszEnumLabel = CPLGetXMLValue(
347 27918 : psListedValue, "label", nullptr);
348 27918 : const char *pszEnumCode = CPLGetXMLValue(
349 : psListedValue, "code", nullptr);
350 55836 : if (pszEnumCode && pszEnumLabel &&
351 27918 : CPLGetValueType(pszEnumCode) ==
352 : CPL_VALUE_INTEGER)
353 : {
354 55836 : if (!attr.enumeratedValues
355 : .insert(
356 0 : std::pair<int, std::string>(
357 55836 : atoi(pszEnumCode),
358 55836 : std::move(pszEnumLabel)))
359 27918 : .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 12744 : if (!m_simpleAttributes
383 12744 : .insert(std::pair<std::string, SimpleAttribute>(
384 12744 : pszCode, std::move(attr)))
385 6372 : .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 6372 : }
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 27 : return true;
404 : }
405 :
406 : /************************************************************************/
407 : /* LoadComplexAttributes() */
408 : /************************************************************************/
409 :
410 27 : bool OGRS101FeatureCatalog::LoadComplexAttributes(const char *pszFC,
411 : const CPLXMLNode *psRoot)
412 : {
413 : const CPLXMLNode *psComplexAttrs =
414 27 : CPLGetXMLNode(psRoot, "S100_FC_ComplexAttributes");
415 27 : if (!psComplexAttrs)
416 : {
417 0 : return EMIT_ERROR_OR_WARNING(
418 : CPLSPrintf("Cannot find S100_FC_ComplexAttributes in %s", pszFC));
419 : }
420 27 : for (const CPLXMLNode *psComplexAttr = psComplexAttrs->psChild;
421 1161 : psComplexAttr; psComplexAttr = psComplexAttr->psNext)
422 : {
423 1134 : if (psComplexAttr->eType == CXT_Element &&
424 1134 : strcmp(psComplexAttr->pszValue, "S100_FC_ComplexAttribute") == 0)
425 : {
426 : const char *pszCode =
427 1134 : CPLGetXMLValue(psComplexAttr, "code", nullptr);
428 1134 : if (pszCode)
429 : {
430 1134 : ComplexAttribute attr;
431 1134 : attr.code = pszCode;
432 1134 : attr.name = CPLGetXMLValue(psComplexAttr, "name", "");
433 : attr.definition =
434 1134 : CPLGetXMLValue(psComplexAttr, "definition", "");
435 :
436 : const CPLXMLNode *psSubAttributeBinding =
437 1134 : CPLGetXMLNode(psComplexAttr, "subAttributeBinding");
438 4239 : for (; psSubAttributeBinding;
439 3105 : psSubAttributeBinding = psSubAttributeBinding->psNext)
440 : {
441 3105 : if (psSubAttributeBinding->eType == CXT_Element &&
442 3105 : strcmp(psSubAttributeBinding->pszValue,
443 : "subAttributeBinding") == 0)
444 : {
445 3105 : const char *pszAttributeRef = CPLGetXMLValue(
446 : psSubAttributeBinding, "attribute.ref", nullptr);
447 3105 : if (pszAttributeRef)
448 : {
449 3105 : attr.attributeBindings.insert(pszAttributeRef);
450 : }
451 : }
452 : }
453 2268 : if (!m_complexAttributes
454 2268 : .insert(std::pair<std::string, ComplexAttribute>(
455 2268 : pszCode, std::move(attr)))
456 1134 : .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 1161 : for (const auto &[code, def] : m_complexAttributes)
475 : {
476 4239 : for (const auto &attrCode : def.attributeBindings)
477 : {
478 3726 : if (!cpl::contains(m_simpleAttributes, attrCode) &&
479 621 : !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 27 : return true;
493 : }
494 :
495 : /************************************************************************/
496 : /* LoadInformationTypes() */
497 : /************************************************************************/
498 :
499 27 : bool OGRS101FeatureCatalog::LoadInformationTypes(const char *pszFC,
500 : const CPLXMLNode *psRoot)
501 : {
502 : const CPLXMLNode *psInformationTypes =
503 27 : CPLGetXMLNode(psRoot, "S100_FC_InformationTypes");
504 27 : if (!psInformationTypes)
505 : {
506 0 : return EMIT_ERROR_OR_WARNING(
507 : CPLSPrintf("Cannot find S100_FC_InformationTypes in %s", pszFC));
508 : }
509 27 : for (const CPLXMLNode *psInformationType = psInformationTypes->psChild;
510 162 : psInformationType; psInformationType = psInformationType->psNext)
511 : {
512 135 : if (psInformationType->eType == CXT_Element &&
513 135 : strcmp(psInformationType->pszValue, "S100_FC_InformationType") == 0)
514 : {
515 : const char *pszCode =
516 135 : CPLGetXMLValue(psInformationType, "code", nullptr);
517 135 : if (pszCode)
518 : {
519 135 : InformationType featureType;
520 135 : featureType.code = pszCode;
521 : featureType.name =
522 135 : CPLGetXMLValue(psInformationType, "name", "");
523 : featureType.definition =
524 135 : CPLGetXMLValue(psInformationType, "definition", "");
525 : featureType.alias =
526 135 : CPLGetXMLValue(psInformationType, "alias", "");
527 :
528 : const CPLXMLNode *psAttributeBinding =
529 135 : CPLGetXMLNode(psInformationType, "attributeBinding");
530 756 : for (; psAttributeBinding;
531 621 : psAttributeBinding = psAttributeBinding->psNext)
532 : {
533 621 : if (psAttributeBinding->eType == CXT_Element &&
534 621 : strcmp(psAttributeBinding->pszValue,
535 : "attributeBinding") == 0)
536 : {
537 621 : const char *pszAttributeRef = CPLGetXMLValue(
538 621 : psAttributeBinding, "attribute.ref", nullptr);
539 621 : if (pszAttributeRef)
540 : {
541 621 : if (!cpl::contains(m_simpleAttributes,
542 1026 : pszAttributeRef) &&
543 405 : !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 621 : pszAttributeRef);
557 : }
558 : }
559 : }
560 :
561 270 : if (!m_informationTypes
562 270 : .insert(std::pair<std::string, InformationType>(
563 270 : pszCode, std::move(featureType)))
564 135 : .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 27 : return true;
583 : }
584 :
585 : /************************************************************************/
586 : /* LoadFeatureTypes() */
587 : /************************************************************************/
588 :
589 27 : bool OGRS101FeatureCatalog::LoadFeatureTypes(const char *pszFC,
590 : const CPLXMLNode *psRoot)
591 : {
592 : const CPLXMLNode *psFeatureTypes =
593 27 : CPLGetXMLNode(psRoot, "S100_FC_FeatureTypes");
594 27 : if (!psFeatureTypes)
595 : {
596 0 : return EMIT_ERROR_OR_WARNING(
597 : CPLSPrintf("Cannot find S100_FC_FeatureTypes in %s", pszFC));
598 : }
599 27 : for (const CPLXMLNode *psFeatureType = psFeatureTypes->psChild;
600 5157 : psFeatureType; psFeatureType = psFeatureType->psNext)
601 : {
602 5130 : if (psFeatureType->eType == CXT_Element &&
603 5130 : strcmp(psFeatureType->pszValue, "S100_FC_FeatureType") == 0)
604 : {
605 : const char *pszCode =
606 5130 : CPLGetXMLValue(psFeatureType, "code", nullptr);
607 5130 : if (pszCode)
608 : {
609 5130 : FeatureType featureType;
610 5130 : featureType.code = pszCode;
611 5130 : featureType.name = CPLGetXMLValue(psFeatureType, "name", "");
612 : featureType.definition =
613 5130 : CPLGetXMLValue(psFeatureType, "definition", "");
614 5130 : featureType.alias = CPLGetXMLValue(psFeatureType, "alias", "");
615 :
616 : const CPLXMLNode *psAttributeBinding =
617 5130 : CPLGetXMLNode(psFeatureType, "attributeBinding");
618 91719 : for (; psAttributeBinding;
619 86589 : psAttributeBinding = psAttributeBinding->psNext)
620 : {
621 86589 : if (psAttributeBinding->eType == CXT_Element &&
622 86589 : strcmp(psAttributeBinding->pszValue,
623 : "attributeBinding") == 0)
624 : {
625 53946 : const char *pszAttributeRef = CPLGetXMLValue(
626 53946 : psAttributeBinding, "attribute.ref", nullptr);
627 53946 : if (pszAttributeRef)
628 : {
629 53946 : if (!cpl::contains(m_simpleAttributes,
630 70713 : pszAttributeRef) &&
631 16767 : !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 53946 : pszAttributeRef);
645 : }
646 : }
647 : }
648 :
649 : const CPLXMLNode *psPermittedPrimitives =
650 5130 : CPLGetXMLNode(psFeatureType, "permittedPrimitives");
651 13419 : for (; psPermittedPrimitives;
652 8289 : psPermittedPrimitives = psPermittedPrimitives->psNext)
653 : {
654 8289 : if (psPermittedPrimitives->eType == CXT_Element &&
655 8289 : strcmp(psPermittedPrimitives->pszValue,
656 8289 : "permittedPrimitives") == 0 &&
657 8289 : psPermittedPrimitives->psChild &&
658 8289 : psPermittedPrimitives->psChild->eType == CXT_Text &&
659 8289 : psPermittedPrimitives->psChild->pszValue)
660 : {
661 8289 : const char *pszPermittedPrimitive =
662 8289 : psPermittedPrimitives->psChild->pszValue;
663 8289 : 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 8289 : if (std::find_if(
672 : std::begin(apszPermittedPrimitives),
673 : std::end(apszPermittedPrimitives),
674 30483 : [pszPermittedPrimitive](const char *x)
675 : {
676 30483 : return strcmp(x, pszPermittedPrimitive) ==
677 30483 : 0;
678 8289 : }) != std::end(apszPermittedPrimitives))
679 : {
680 : featureType.permittedPrimitives.insert(
681 8289 : 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 10260 : if (!m_featureTypes
694 10260 : .insert(std::pair<std::string, FeatureType>(
695 10260 : pszCode, std::move(featureType)))
696 5130 : .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 27 : return true;
715 : }
|