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