Line data Source code
1 : /******************************************************************************
2 : * Project: OGR
3 : * Purpose: OGRGMLASDriver implementation
4 : * Author: Even Rouault, <even dot rouault at spatialys dot com>
5 : *
6 : * Initial development funded by the European Earth observation programme
7 : * Copernicus
8 : *
9 : ******************************************************************************
10 : * Copyright (c) 2016, Even Rouault, <even dot rouault at spatialys dot com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "ogr_gmlas.h"
16 :
17 : #include "memdataset.h"
18 : #include "cpl_sha256.h"
19 :
20 : #include <algorithm>
21 :
22 : /************************************************************************/
23 : /* XercesInitializer::XercesInitializer() */
24 : /************************************************************************/
25 :
26 195 : OGRGMLASDataSource::XercesInitializer::XercesInitializer()
27 : {
28 195 : OGRInitializeXerces();
29 195 : }
30 :
31 : /************************************************************************/
32 : /* XercesInitializer::~XercesInitializer() */
33 : /************************************************************************/
34 :
35 195 : OGRGMLASDataSource::XercesInitializer::~XercesInitializer()
36 : {
37 195 : OGRDeinitializeXerces();
38 195 : }
39 :
40 : /************************************************************************/
41 : /* OGRGMLASDataSource() */
42 : /************************************************************************/
43 :
44 195 : OGRGMLASDataSource::OGRGMLASDataSource()
45 195 : : m_poFieldsMetadataLayer(std::make_unique<OGRMemLayer>(
46 0 : szOGR_FIELDS_METADATA, nullptr, wkbNone)),
47 195 : m_poLayersMetadataLayer(std::make_unique<OGRMemLayer>(
48 0 : szOGR_LAYERS_METADATA, nullptr, wkbNone)),
49 195 : m_poRelationshipsLayer(std::make_unique<OGRMemLayer>(
50 0 : szOGR_LAYER_RELATIONSHIPS, nullptr, wkbNone)),
51 : m_poOtherMetadataLayer(
52 780 : std::make_unique<OGRMemLayer>(szOGR_OTHER_METADATA, nullptr, wkbNone))
53 : {
54 : // Initialize m_poFieldsMetadataLayer
55 : {
56 390 : OGRFieldDefn oFieldDefn(szLAYER_NAME, OFTString);
57 195 : m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
58 : }
59 : {
60 390 : OGRFieldDefn oFieldDefn(szFIELD_INDEX, OFTInteger);
61 195 : m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
62 : }
63 : {
64 390 : OGRFieldDefn oFieldDefn(szFIELD_NAME, OFTString);
65 195 : m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
66 : }
67 : {
68 390 : OGRFieldDefn oFieldDefn(szFIELD_XPATH, OFTString);
69 195 : m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
70 : }
71 : {
72 390 : OGRFieldDefn oFieldDefn(szFIELD_TYPE, OFTString);
73 195 : m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
74 : }
75 : {
76 390 : OGRFieldDefn oFieldDefn(szFIELD_IS_LIST, OFTInteger);
77 195 : oFieldDefn.SetSubType(OFSTBoolean);
78 195 : m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
79 : }
80 : {
81 390 : OGRFieldDefn oFieldDefn(szFIELD_MIN_OCCURS, OFTInteger);
82 195 : m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
83 : }
84 : {
85 390 : OGRFieldDefn oFieldDefn(szFIELD_MAX_OCCURS, OFTInteger);
86 195 : m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
87 : }
88 : {
89 390 : OGRFieldDefn oFieldDefn(szFIELD_REPETITION_ON_SEQUENCE, OFTInteger);
90 195 : oFieldDefn.SetSubType(OFSTBoolean);
91 195 : m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
92 : }
93 : {
94 390 : OGRFieldDefn oFieldDefn(szFIELD_DEFAULT_VALUE, OFTString);
95 195 : m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
96 : }
97 : {
98 390 : OGRFieldDefn oFieldDefn(szFIELD_FIXED_VALUE, OFTString);
99 195 : m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
100 : }
101 : {
102 390 : OGRFieldDefn oFieldDefn(szFIELD_CATEGORY, OFTString);
103 195 : m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
104 : }
105 : {
106 390 : OGRFieldDefn oFieldDefn(szFIELD_RELATED_LAYER, OFTString);
107 195 : m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
108 : }
109 : {
110 390 : OGRFieldDefn oFieldDefn(szFIELD_JUNCTION_LAYER, OFTString);
111 195 : m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
112 : }
113 : {
114 390 : OGRFieldDefn oFieldDefn(szFIELD_DOCUMENTATION, OFTString);
115 195 : m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
116 : }
117 :
118 : // Initialize m_poLayersMetadataLayer
119 : {
120 390 : OGRFieldDefn oFieldDefn(szLAYER_NAME, OFTString);
121 195 : m_poLayersMetadataLayer->CreateField(&oFieldDefn);
122 : }
123 : {
124 390 : OGRFieldDefn oFieldDefn(szLAYER_XPATH, OFTString);
125 195 : m_poLayersMetadataLayer->CreateField(&oFieldDefn);
126 : }
127 : {
128 390 : OGRFieldDefn oFieldDefn(szLAYER_CATEGORY, OFTString);
129 195 : m_poLayersMetadataLayer->CreateField(&oFieldDefn);
130 : }
131 : {
132 390 : OGRFieldDefn oFieldDefn(szLAYER_PKID_NAME, OFTString);
133 195 : m_poLayersMetadataLayer->CreateField(&oFieldDefn);
134 : }
135 : {
136 390 : OGRFieldDefn oFieldDefn(szLAYER_PARENT_PKID_NAME, OFTString);
137 195 : m_poLayersMetadataLayer->CreateField(&oFieldDefn);
138 : }
139 : {
140 390 : OGRFieldDefn oFieldDefn(szLAYER_DOCUMENTATION, OFTString);
141 195 : m_poLayersMetadataLayer->CreateField(&oFieldDefn);
142 : }
143 :
144 : // Initialize m_poRelationshipsLayer
145 : {
146 390 : OGRFieldDefn oFieldDefn(szPARENT_LAYER, OFTString);
147 195 : m_poRelationshipsLayer->CreateField(&oFieldDefn);
148 : }
149 : {
150 390 : OGRFieldDefn oFieldDefn(szPARENT_PKID, OFTString);
151 195 : m_poRelationshipsLayer->CreateField(&oFieldDefn);
152 : }
153 : {
154 390 : OGRFieldDefn oFieldDefn(szPARENT_ELEMENT_NAME, OFTString);
155 195 : m_poRelationshipsLayer->CreateField(&oFieldDefn);
156 : }
157 : {
158 390 : OGRFieldDefn oFieldDefn(szCHILD_LAYER, OFTString);
159 195 : m_poRelationshipsLayer->CreateField(&oFieldDefn);
160 : }
161 : {
162 390 : OGRFieldDefn oFieldDefn(szCHILD_PKID, OFTString);
163 195 : m_poRelationshipsLayer->CreateField(&oFieldDefn);
164 : }
165 :
166 : // Initialize m_poOtherMetadataLayer
167 : {
168 390 : OGRFieldDefn oFieldDefn(szKEY, OFTString);
169 195 : m_poOtherMetadataLayer->CreateField(&oFieldDefn);
170 : }
171 : {
172 390 : OGRFieldDefn oFieldDefn(szVALUE, OFTString);
173 195 : m_poOtherMetadataLayer->CreateField(&oFieldDefn);
174 : }
175 195 : }
176 :
177 : /************************************************************************/
178 : /* ~OGRGMLASDataSource() */
179 : /************************************************************************/
180 :
181 390 : OGRGMLASDataSource::~OGRGMLASDataSource()
182 : {
183 195 : if (m_bUnlinkConfigFileAfterUse)
184 : {
185 0 : VSIUnlink(m_osConfigFile.c_str());
186 : }
187 390 : }
188 :
189 : /************************************************************************/
190 : /* GetLayerCount() */
191 : /************************************************************************/
192 :
193 8514 : int OGRGMLASDataSource::GetLayerCount()
194 : {
195 8514 : return static_cast<int>(m_apoLayers.size() +
196 8514 : m_apoRequestedMetadataLayers.size());
197 : }
198 :
199 : /************************************************************************/
200 : /* GetLayer() */
201 : /************************************************************************/
202 :
203 8906 : OGRLayer *OGRGMLASDataSource::GetLayer(int i)
204 : {
205 8906 : const int nBaseLayers = static_cast<int>(m_apoLayers.size());
206 8906 : if (i >= nBaseLayers)
207 : {
208 153 : RunFirstPassIfNeeded(nullptr, nullptr, nullptr);
209 153 : if (i - nBaseLayers <
210 153 : static_cast<int>(m_apoRequestedMetadataLayers.size()))
211 152 : return m_apoRequestedMetadataLayers[i - nBaseLayers];
212 : }
213 :
214 8754 : if (i < 0 || i >= nBaseLayers)
215 2 : return nullptr;
216 8752 : return m_apoLayers[i].get();
217 : }
218 :
219 : /************************************************************************/
220 : /* GetLayerByName() */
221 : /************************************************************************/
222 :
223 600 : OGRLayer *OGRGMLASDataSource::GetLayerByName(const char *pszName)
224 : {
225 600 : if (OGRLayer *poLayer = GDALDataset::GetLayerByName(pszName))
226 576 : return poLayer;
227 :
228 : OGRLayer *apoLayers[] = {
229 24 : m_poFieldsMetadataLayer.get(), m_poLayersMetadataLayer.get(),
230 24 : m_poRelationshipsLayer.get(), m_poOtherMetadataLayer.get()};
231 52 : for (auto *poLayer : apoLayers)
232 : {
233 51 : if (EQUAL(pszName, poLayer->GetName()))
234 : {
235 23 : if (std::find(m_apoRequestedMetadataLayers.begin(),
236 : m_apoRequestedMetadataLayers.end(),
237 23 : poLayer) == m_apoRequestedMetadataLayers.end())
238 : {
239 23 : m_apoRequestedMetadataLayers.push_back(poLayer);
240 : }
241 23 : RunFirstPassIfNeeded(nullptr, nullptr, nullptr);
242 23 : return poLayer;
243 : }
244 : }
245 :
246 1 : return nullptr;
247 : }
248 :
249 : /************************************************************************/
250 : /* TranslateClasses() */
251 : /************************************************************************/
252 :
253 16958 : void OGRGMLASDataSource::TranslateClasses(OGRGMLASLayer *poParentLayer,
254 : const GMLASFeatureClass &oFC)
255 : {
256 16958 : const std::vector<GMLASFeatureClass> &aoClasses = oFC.GetNestedClasses();
257 :
258 : // CPLDebug("GMLAS", "TranslateClasses(%s,%s)",
259 : // oFC.GetName().c_str(), oFC.GetXPath().c_str());
260 :
261 16958 : m_apoLayers.emplace_back(std::make_unique<OGRGMLASLayer>(
262 16958 : this, oFC, poParentLayer, m_oConf.m_bAlwaysGenerateOGRId));
263 16958 : auto poLayer = m_apoLayers.back().get();
264 :
265 20738 : for (size_t i = 0; i < aoClasses.size(); ++i)
266 : {
267 3780 : TranslateClasses(poLayer, aoClasses[i]);
268 : }
269 16958 : }
270 :
271 : /************************************************************************/
272 : /* GMLASTopElementParser */
273 : /************************************************************************/
274 :
275 : class GMLASTopElementParser : public DefaultHandler
276 : {
277 : std::vector<PairURIFilename> m_aoFilenames{};
278 : int m_nStartElementCounter = 0;
279 : bool m_bFinish = false;
280 : bool m_bFoundSWE = false;
281 : std::map<CPLString, CPLString> m_oMapDocNSURIToPrefix{};
282 :
283 : public:
284 190 : GMLASTopElementParser() = default;
285 :
286 : void Parse(const CPLString &osFilename,
287 : const std::shared_ptr<VSIVirtualHandle> &fp);
288 :
289 155 : const std::vector<PairURIFilename> &GetXSDs() const
290 : {
291 155 : return m_aoFilenames;
292 : }
293 :
294 158 : bool GetSWE() const
295 : {
296 158 : return m_bFoundSWE;
297 : }
298 :
299 162 : const std::map<CPLString, CPLString> &GetMapDocNSURIToPrefix() const
300 : {
301 162 : return m_oMapDocNSURIToPrefix;
302 : }
303 :
304 : virtual void startElement(const XMLCh *const uri,
305 : const XMLCh *const localname,
306 : const XMLCh *const qname,
307 : const Attributes &attrs) override;
308 : };
309 :
310 : /************************************************************************/
311 : /* Parse() */
312 : /************************************************************************/
313 :
314 158 : void GMLASTopElementParser::Parse(const CPLString &osFilename,
315 : const std::shared_ptr<VSIVirtualHandle> &fp)
316 : {
317 : auto poSAXReader =
318 316 : std::unique_ptr<SAX2XMLReader>(XMLReaderFactory::createXMLReader());
319 :
320 158 : poSAXReader->setFeature(XMLUni::fgSAX2CoreNameSpaces, true);
321 158 : poSAXReader->setFeature(XMLUni::fgSAX2CoreNameSpacePrefixes, true);
322 :
323 158 : poSAXReader->setContentHandler(this);
324 158 : poSAXReader->setLexicalHandler(this);
325 158 : poSAXReader->setDTDHandler(this);
326 :
327 158 : poSAXReader->setFeature(XMLUni::fgXercesLoadSchema, false);
328 :
329 316 : GMLASErrorHandler oErrorHandler;
330 158 : poSAXReader->setErrorHandler(&oErrorHandler);
331 :
332 316 : GMLASInputSource oIS(osFilename, fp);
333 :
334 : try
335 : {
336 316 : XMLPScanToken oToFill;
337 158 : if (poSAXReader->parseFirst(oIS, oToFill))
338 : {
339 316 : while (!m_bFinish && poSAXReader->parseNext(oToFill))
340 : {
341 : // do nothing
342 : }
343 : }
344 : }
345 0 : catch (const XMLException &toCatch)
346 : {
347 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
348 0 : transcode(toCatch.getMessage()).c_str());
349 : }
350 0 : catch (const SAXException &toCatch)
351 : {
352 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
353 0 : transcode(toCatch.getMessage()).c_str());
354 : }
355 158 : }
356 :
357 : /************************************************************************/
358 : /* startElement() */
359 : /************************************************************************/
360 :
361 158 : void GMLASTopElementParser::startElement(const XMLCh *const /*uri*/,
362 : const XMLCh *const /*localname*/,
363 : const XMLCh *const /*qname*/,
364 : const Attributes &attrs)
365 : {
366 158 : m_nStartElementCounter++;
367 :
368 985 : for (unsigned int i = 0; i < attrs.getLength(); i++)
369 : {
370 1654 : const std::string osAttrURIPrefix(transcode(attrs.getURI(i)));
371 1654 : const std::string osAttrLocalname(transcode(attrs.getLocalName(i)));
372 1654 : const std::string osAttrValue(transcode(attrs.getValue(i)));
373 :
374 983 : if (osAttrURIPrefix == szXSI_URI &&
375 156 : osAttrLocalname == szSCHEMA_LOCATION)
376 : {
377 130 : CPLDebug("GMLAS", "%s=%s", szSCHEMA_LOCATION, osAttrValue.c_str());
378 :
379 : const CPLStringList aosTokens(
380 260 : CSLTokenizeString2(osAttrValue.c_str(), " ", 0));
381 130 : const int nTokens = aosTokens.size();
382 130 : if ((nTokens % 2) == 0)
383 : {
384 343 : for (int j = 0; j < nTokens; j += 2)
385 : {
386 422 : if (!STARTS_WITH(aosTokens[j], szWFS_URI) &&
387 209 : !(EQUAL(aosTokens[j], szGML_URI) ||
388 422 : STARTS_WITH(aosTokens[j],
389 : (CPLString(szGML_URI) + "/").c_str())))
390 : {
391 209 : CPLDebug("GMLAS", "Schema to analyze: %s -> %s",
392 : aosTokens[j], aosTokens[j + 1]);
393 209 : m_aoFilenames.push_back(
394 418 : PairURIFilename(aosTokens[j], aosTokens[j + 1]));
395 : }
396 : }
397 : }
398 : }
399 723 : else if (osAttrURIPrefix == szXSI_URI &&
400 26 : osAttrLocalname == szNO_NAMESPACE_SCHEMA_LOCATION)
401 : {
402 26 : CPLDebug("GMLAS", "%s=%s", szNO_NAMESPACE_SCHEMA_LOCATION,
403 : osAttrValue.c_str());
404 26 : m_aoFilenames.push_back(PairURIFilename("", osAttrValue));
405 : }
406 671 : else if (osAttrURIPrefix == szXMLNS_URI && osAttrValue == szSWE_URI)
407 : {
408 3 : CPLDebug("GMLAS", "SWE namespace found");
409 3 : m_bFoundSWE = true;
410 : }
411 1128 : else if (osAttrURIPrefix == szXMLNS_URI && !osAttrValue.empty() &&
412 460 : !osAttrLocalname.empty())
413 : {
414 : #ifdef DEBUG_VERBOSE
415 : CPLDebug("GMLAS", "Namespace %s = %s", osAttrLocalname.c_str(),
416 : osAttrValue.c_str());
417 : #endif
418 460 : m_oMapDocNSURIToPrefix[osAttrValue] = osAttrLocalname;
419 : }
420 : }
421 :
422 158 : if (m_nStartElementCounter == 1)
423 158 : m_bFinish = true;
424 158 : }
425 :
426 : /************************************************************************/
427 : /* FillOtherMetadataLayer() */
428 : /************************************************************************/
429 :
430 178 : void OGRGMLASDataSource::FillOtherMetadataLayer(
431 : GDALOpenInfo *poOpenInfo, const CPLString &osConfigFile,
432 : const std::vector<PairURIFilename> &aoXSDs,
433 : const std::set<CPLString> &oSetSchemaURLs)
434 : {
435 : // 2 "secret" options just used for tests
436 178 : const bool bKeepRelativePathsForMetadata = CPLTestBool(
437 178 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
438 : szKEEP_RELATIVE_PATHS_FOR_METADATA_OPTION, "NO"));
439 :
440 178 : const bool bExposeConfiguration = CPLTestBool(
441 178 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
442 : szEXPOSE_CONFIGURATION_IN_METADATA_OPTION, "YES"));
443 :
444 178 : const bool bExposeSchemaNames = CPLTestBool(
445 178 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
446 : szEXPOSE_SCHEMAS_NAME_IN_METADATA_OPTION, "YES"));
447 :
448 178 : OGRFeatureDefn *poFDefn = m_poOtherMetadataLayer->GetLayerDefn();
449 :
450 178 : if (!osConfigFile.empty() && bExposeConfiguration)
451 : {
452 176 : if (STARTS_WITH(osConfigFile, "<Configuration"))
453 : {
454 85 : OGRFeature oFeature(poFDefn);
455 85 : oFeature.SetField(szKEY, szCONFIGURATION_INLINED);
456 85 : oFeature.SetField(szVALUE, osConfigFile.c_str());
457 85 : CPL_IGNORE_RET_VAL(
458 85 : m_poOtherMetadataLayer->CreateFeature(&oFeature));
459 : }
460 : else
461 : {
462 : {
463 91 : OGRFeature oFeature(poFDefn);
464 91 : oFeature.SetField(szKEY, szCONFIGURATION_FILENAME);
465 91 : char *pszCurDir = CPLGetCurrentDir();
466 273 : if (!bKeepRelativePathsForMetadata &&
467 91 : CPLIsFilenameRelative(osConfigFile) && pszCurDir != nullptr)
468 : {
469 0 : oFeature.SetField(
470 : szVALUE,
471 0 : CPLFormFilenameSafe(pszCurDir, osConfigFile, nullptr)
472 : .c_str());
473 : }
474 : else
475 : {
476 91 : oFeature.SetField(szVALUE, osConfigFile.c_str());
477 : }
478 91 : CPLFree(pszCurDir);
479 91 : CPL_IGNORE_RET_VAL(
480 91 : m_poOtherMetadataLayer->CreateFeature(&oFeature));
481 : }
482 :
483 91 : GByte *pabyRet = nullptr;
484 91 : if (VSIIngestFile(nullptr, osConfigFile, &pabyRet, nullptr, -1))
485 : {
486 91 : OGRFeature oFeature(poFDefn);
487 91 : oFeature.SetField(szKEY, szCONFIGURATION_INLINED);
488 91 : oFeature.SetField(szVALUE, reinterpret_cast<char *>(pabyRet));
489 91 : CPL_IGNORE_RET_VAL(
490 91 : m_poOtherMetadataLayer->CreateFeature(&oFeature));
491 : }
492 91 : VSIFree(pabyRet);
493 : }
494 : }
495 :
496 178 : const char *const apszMeaningfulOptionsToStoreInMD[] = {
497 : szSWAP_COORDINATES_OPTION, szREMOVE_UNUSED_LAYERS_OPTION,
498 : szREMOVE_UNUSED_FIELDS_OPTION};
499 712 : for (size_t i = 0; i < CPL_ARRAYSIZE(apszMeaningfulOptionsToStoreInMD); ++i)
500 : {
501 534 : const char *pszKey = apszMeaningfulOptionsToStoreInMD[i];
502 : const char *pszVal =
503 534 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, pszKey);
504 534 : if (pszVal)
505 : {
506 4 : OGRFeature oFeature(poFDefn);
507 4 : oFeature.SetField(szKEY, pszKey);
508 4 : oFeature.SetField(szVALUE, pszVal);
509 4 : CPL_IGNORE_RET_VAL(
510 4 : m_poOtherMetadataLayer->CreateFeature(&oFeature));
511 : }
512 : }
513 :
514 356 : CPLString osAbsoluteGMLFilename;
515 178 : if (!m_osGMLFilename.empty())
516 : {
517 150 : OGRFeature oFeature(poFDefn);
518 150 : oFeature.SetField(szKEY, szDOCUMENT_FILENAME);
519 150 : char *pszCurDir = CPLGetCurrentDir();
520 448 : if (!bKeepRelativePathsForMetadata &&
521 150 : CPLIsFilenameRelative(m_osGMLFilename) && pszCurDir != nullptr)
522 : {
523 : osAbsoluteGMLFilename =
524 79 : CPLFormFilenameSafe(pszCurDir, m_osGMLFilename, nullptr);
525 : }
526 : else
527 71 : osAbsoluteGMLFilename = m_osGMLFilename;
528 150 : oFeature.SetField(szVALUE, osAbsoluteGMLFilename.c_str());
529 150 : CPLFree(pszCurDir);
530 150 : CPL_IGNORE_RET_VAL(m_poOtherMetadataLayer->CreateFeature(&oFeature));
531 : }
532 :
533 178 : int nNSIdx = 1;
534 356 : std::set<CPLString> oSetVisitedURI;
535 450 : for (int i = 0; i < static_cast<int>(aoXSDs.size()); i++)
536 : {
537 272 : const CPLString osURI(aoXSDs[i].first);
538 272 : const std::string osXSDFilename(aoXSDs[i].second);
539 :
540 272 : oSetVisitedURI.insert(osURI);
541 :
542 272 : if (osURI == szOGRGMLAS_URI)
543 9 : continue;
544 :
545 : {
546 263 : OGRFeature oFeature(poFDefn);
547 263 : oFeature.SetField(szKEY, CPLSPrintf(szNAMESPACE_URI_FMT, nNSIdx));
548 263 : oFeature.SetField(szVALUE, osURI.c_str());
549 263 : CPL_IGNORE_RET_VAL(
550 263 : m_poOtherMetadataLayer->CreateFeature(&oFeature));
551 : }
552 :
553 : {
554 526 : OGRFeature oFeature(poFDefn);
555 263 : oFeature.SetField(szKEY,
556 : CPLSPrintf(szNAMESPACE_LOCATION_FMT, nNSIdx));
557 :
558 : const CPLString osAbsoluteXSDFilename(
559 251 : (osXSDFilename.find("http://") != 0 &&
560 251 : osXSDFilename.find("https://") != 0 &&
561 132 : CPLIsFilenameRelative(osXSDFilename.c_str()))
562 604 : ? CPLFormFilenameSafe(
563 353 : CPLGetDirnameSafe(osAbsoluteGMLFilename).c_str(),
564 : osXSDFilename.c_str(), nullptr)
565 263 : : osXSDFilename);
566 263 : oFeature.SetField(szVALUE, osAbsoluteXSDFilename.c_str());
567 263 : CPL_IGNORE_RET_VAL(
568 263 : m_poOtherMetadataLayer->CreateFeature(&oFeature));
569 : }
570 :
571 263 : if (m_oMapURIToPrefix.find(osURI) != m_oMapURIToPrefix.end())
572 : {
573 235 : OGRFeature oFeature(poFDefn);
574 235 : oFeature.SetField(szKEY,
575 : CPLSPrintf(szNAMESPACE_PREFIX_FMT, nNSIdx));
576 235 : oFeature.SetField(szVALUE, m_oMapURIToPrefix[osURI].c_str());
577 235 : CPL_IGNORE_RET_VAL(
578 235 : m_poOtherMetadataLayer->CreateFeature(&oFeature));
579 : }
580 :
581 263 : nNSIdx++;
582 : }
583 :
584 1111 : for (const auto &oIter : m_oMapURIToPrefix)
585 : {
586 933 : const CPLString &osURI(oIter.first);
587 933 : const CPLString &osPrefix(oIter.second);
588 :
589 1622 : if (oSetVisitedURI.find(osURI) == oSetVisitedURI.end() &&
590 1712 : osURI != szXML_URI && osURI != szXS_URI && osURI != szXSI_URI &&
591 1956 : osURI != szXMLNS_URI && osURI != szOGRGMLAS_URI)
592 : {
593 : {
594 156 : OGRFeature oFeature(poFDefn);
595 156 : oFeature.SetField(szKEY,
596 : CPLSPrintf(szNAMESPACE_URI_FMT, nNSIdx));
597 156 : oFeature.SetField(szVALUE, osURI.c_str());
598 156 : CPL_IGNORE_RET_VAL(
599 156 : m_poOtherMetadataLayer->CreateFeature(&oFeature));
600 : }
601 :
602 : {
603 156 : OGRFeature oFeature(poFDefn);
604 156 : oFeature.SetField(szKEY,
605 : CPLSPrintf(szNAMESPACE_PREFIX_FMT, nNSIdx));
606 156 : oFeature.SetField(szVALUE, osPrefix.c_str());
607 156 : CPL_IGNORE_RET_VAL(
608 156 : m_poOtherMetadataLayer->CreateFeature(&oFeature));
609 : }
610 :
611 156 : nNSIdx++;
612 : }
613 : }
614 :
615 178 : if (!m_osGMLVersionFound.empty())
616 : {
617 5 : OGRFeature oFeature(poFDefn);
618 5 : oFeature.SetField(szKEY, szGML_VERSION);
619 5 : oFeature.SetField(szVALUE, m_osGMLVersionFound);
620 5 : CPL_IGNORE_RET_VAL(m_poOtherMetadataLayer->CreateFeature(&oFeature));
621 : }
622 :
623 178 : int nSchemaIdx = 1;
624 178 : if (bExposeSchemaNames)
625 : {
626 571 : for (const auto &osSchemaURL : oSetSchemaURLs)
627 : {
628 395 : OGRFeature oFeature(poFDefn);
629 395 : oFeature.SetField(szKEY, CPLSPrintf(szSCHEMA_NAME_FMT, nSchemaIdx));
630 395 : oFeature.SetField(szVALUE, osSchemaURL.c_str());
631 395 : CPL_IGNORE_RET_VAL(
632 395 : m_poOtherMetadataLayer->CreateFeature(&oFeature));
633 :
634 395 : nSchemaIdx++;
635 : }
636 : }
637 178 : }
638 :
639 : /************************************************************************/
640 : /* BuildXSDVector() */
641 : /************************************************************************/
642 :
643 : std::vector<PairURIFilename>
644 35 : OGRGMLASDataSource::BuildXSDVector(const CPLString &osXSDFilenames)
645 : {
646 35 : std::vector<PairURIFilename> aoXSDs;
647 35 : char **papszTokens = CSLTokenizeString2(osXSDFilenames, ",", 0);
648 35 : char *pszCurDir = CPLGetCurrentDir();
649 70 : for (int i = 0; papszTokens != nullptr && papszTokens[i] != nullptr; i++)
650 : {
651 104 : if (!STARTS_WITH(papszTokens[i], "http://") &&
652 34 : !STARTS_WITH(papszTokens[i], "https://") &&
653 69 : CPLIsFilenameRelative(papszTokens[i]) && pszCurDir != nullptr)
654 : {
655 29 : aoXSDs.push_back(PairURIFilename(
656 58 : "", CPLFormFilenameSafe(pszCurDir, papszTokens[i], nullptr)
657 58 : .c_str()));
658 : }
659 : else
660 : {
661 6 : aoXSDs.push_back(PairURIFilename("", papszTokens[i]));
662 : }
663 : }
664 35 : CPLFree(pszCurDir);
665 35 : CSLDestroy(papszTokens);
666 35 : return aoXSDs;
667 : }
668 :
669 : /************************************************************************/
670 : /* Open() */
671 : /************************************************************************/
672 :
673 195 : bool OGRGMLASDataSource::Open(GDALOpenInfo *poOpenInfo)
674 : {
675 195 : m_osConfigFile = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
676 195 : szCONFIG_FILE_OPTION, "");
677 195 : if (m_osConfigFile.empty())
678 : {
679 : m_osConfigFile =
680 104 : GMLASConfiguration::GetDefaultConfFile(m_bUnlinkConfigFileAfterUse);
681 : }
682 195 : if (m_osConfigFile.empty())
683 : {
684 0 : CPLError(CE_Warning, CPLE_AppDefined,
685 : "No configuration file found. Using hard-coded defaults");
686 0 : m_oConf.Finalize();
687 : }
688 : else
689 : {
690 195 : if (!m_oConf.Load(m_osConfigFile.c_str()))
691 : {
692 2 : CPLError(CE_Failure, CPLE_AppDefined,
693 : "Loading of configuration failed");
694 2 : return false;
695 : }
696 : }
697 :
698 193 : m_oCache.SetCacheDirectory(m_oConf.m_osXSDCacheDirectory);
699 193 : const bool bRefreshCache(CPLTestBool(CSLFetchNameValueDef(
700 193 : poOpenInfo->papszOpenOptions, szREFRESH_CACHE_OPTION, "NO")));
701 193 : m_oCache.SetRefreshMode(bRefreshCache);
702 193 : m_oCache.SetAllowDownload(m_oConf.m_bAllowRemoteSchemaDownload);
703 :
704 193 : m_oIgnoredXPathMatcher.SetRefXPaths(m_oConf.m_oMapPrefixToURIIgnoredXPaths,
705 193 : m_oConf.m_aosIgnoredXPaths);
706 :
707 : {
708 386 : std::vector<CPLString> oVector;
709 300 : for (const auto &oIter : m_oConf.m_oMapChildrenElementsConstraints)
710 107 : oVector.push_back(oIter.first);
711 193 : m_oChildrenElementsConstraintsXPathMatcher.SetRefXPaths(
712 193 : m_oConf.m_oMapPrefixToURITypeConstraints, oVector);
713 : }
714 :
715 193 : m_oForcedFlattenedXPathMatcher.SetRefXPaths(
716 193 : m_oConf.m_oMapPrefixToURIFlatteningRules,
717 193 : m_oConf.m_osForcedFlattenedXPath);
718 :
719 193 : m_oDisabledFlattenedXPathMatcher.SetRefXPaths(
720 193 : m_oConf.m_oMapPrefixToURIFlatteningRules,
721 193 : m_oConf.m_osDisabledFlattenedXPath);
722 :
723 : GMLASSchemaAnalyzer oAnalyzer(
724 193 : m_oIgnoredXPathMatcher, m_oChildrenElementsConstraintsXPathMatcher,
725 193 : m_oConf.m_oMapChildrenElementsConstraints,
726 386 : m_oForcedFlattenedXPathMatcher, m_oDisabledFlattenedXPathMatcher);
727 193 : oAnalyzer.SetUseArrays(m_oConf.m_bUseArrays);
728 193 : oAnalyzer.SetUseNullState(m_oConf.m_bUseNullState);
729 193 : oAnalyzer.SetInstantiateGMLFeaturesOnly(
730 193 : m_oConf.m_bInstantiateGMLFeaturesOnly);
731 193 : oAnalyzer.SetIdentifierMaxLength(m_oConf.m_nIdentifierMaxLength);
732 193 : oAnalyzer.SetCaseInsensitiveIdentifier(
733 193 : m_oConf.m_bCaseInsensitiveIdentifier);
734 193 : oAnalyzer.SetPGIdentifierLaundering(m_oConf.m_bPGIdentifierLaundering);
735 193 : oAnalyzer.SetMaximumFieldsForFlattening(
736 : m_oConf.m_nMaximumFieldsForFlattening);
737 193 : oAnalyzer.SetAlwaysGenerateOGRId(m_oConf.m_bAlwaysGenerateOGRId);
738 :
739 193 : m_osGMLFilename = STARTS_WITH_CI(poOpenInfo->pszFilename, szGMLAS_PREFIX)
740 386 : ? CPLExpandTildeSafe(poOpenInfo->pszFilename +
741 : strlen(szGMLAS_PREFIX))
742 193 : : poOpenInfo->pszFilename;
743 :
744 : CPLString osXSDFilenames =
745 386 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, szXSD_OPTION, "");
746 :
747 193 : std::shared_ptr<VSIVirtualHandle> fpGML;
748 193 : if (!m_osGMLFilename.empty())
749 : {
750 160 : fpGML.reset(VSIFOpenL(m_osGMLFilename, "rb"), VSIVirtualHandleCloser{});
751 160 : if (fpGML == nullptr)
752 : {
753 2 : CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
754 : m_osGMLFilename.c_str());
755 2 : return false;
756 : }
757 : }
758 33 : else if (osXSDFilenames.empty())
759 : {
760 1 : CPLError(CE_Failure, CPLE_AppDefined,
761 : "%s open option must be provided when no "
762 : "XML data file is passed",
763 : szXSD_OPTION);
764 1 : return false;
765 : }
766 :
767 380 : GMLASTopElementParser topElementParser;
768 190 : if (!m_osGMLFilename.empty())
769 : {
770 158 : topElementParser.Parse(m_osGMLFilename, fpGML);
771 158 : if (m_oConf.m_eSWEActivationMode ==
772 : GMLASConfiguration::SWE_ACTIVATE_IF_NAMESPACE_FOUND)
773 : {
774 158 : m_bFoundSWE = topElementParser.GetSWE();
775 : }
776 0 : else if (m_oConf.m_eSWEActivationMode ==
777 : GMLASConfiguration::SWE_ACTIVATE_TRUE)
778 : {
779 0 : m_bFoundSWE = true;
780 : }
781 158 : oAnalyzer.SetMapDocNSURIToPrefix(
782 : topElementParser.GetMapDocNSURIToPrefix());
783 : }
784 380 : std::vector<PairURIFilename> aoXSDs;
785 190 : if (osXSDFilenames.empty())
786 : {
787 155 : aoXSDs = topElementParser.GetXSDs();
788 155 : if (aoXSDs.empty())
789 : {
790 : const std::map<std::string, std::string> mapWellKnownURIToLocation =
791 : {
792 : {"http://www.opengis.net/citygml/2.0",
793 : "https://schemas.opengis.net/citygml/2.0/cityGMLBase.xsd"},
794 : {"http://www.opengis.net/citygml/appearance/2.0",
795 : "https://schemas.opengis.net/citygml/appearance/2.0/"
796 : "appearance.xsd"},
797 : {"http://www.opengis.net/citygml/bridge/2.0",
798 : "https://schemas.opengis.net/citygml/bridge/2.0/"
799 : "bridge.xsd"},
800 : {"http://www.opengis.net/citygml/building/2.0",
801 : "https://schemas.opengis.net/citygml/building/2.0/"
802 : "building.xsd"},
803 : {
804 : "http://www.opengis.net/citygml/cityfurniture/2.0",
805 : "https://schemas.opengis.net/citygml/cityfurniture/2.0/"
806 : "cityFurniture.xsd",
807 : },
808 : {"http://www.opengis.net/citygml/cityobjectgroup/2.0",
809 : "https://schemas.opengis.net/citygml/cityobjectgroup/2.0/"
810 : "cityObjectGroup.xsd"},
811 : {"http://www.opengis.net/citygml/generics/2.0",
812 : "https://schemas.opengis.net/citygml/generics/2.0/"
813 : "generics.xsd"},
814 : {"http://www.opengis.net/citygml/landuse/2.0",
815 : "https://schemas.opengis.net/citygml/landuse/2.0/"
816 : "landUse.xsd"},
817 : {"http://www.opengis.net/citygml/relief/2.0",
818 : "https://schemas.opengis.net/citygml/relief/2.0/"
819 : "relief.xsd"},
820 : // { "http://www.opengis.net/citygml/textures/2.0", } ,
821 : {"http://www.opengis.net/citygml/transportation/2.0",
822 : "https://schemas.opengis.net/citygml/transportation/2.0/"
823 : "transportation.xsd"},
824 : {"http://www.opengis.net/citygml/tunnel/2.0",
825 : "https://schemas.opengis.net/citygml/tunnel/2.0/"
826 : "tunnel.xsd"},
827 : {"http://www.opengis.net/citygml/vegetation/2.0",
828 : "https://schemas.opengis.net/citygml/vegetation/2.0/"
829 : "vegetation.xsd"},
830 : {"http://www.opengis.net/citygml/waterbody/2.0",
831 : "https://schemas.opengis.net/citygml/waterbody/2.0/"
832 : "waterBody.xsd"},
833 : {"http://www.w3.org/1999/xlink",
834 : "https://www.w3.org/1999/xlink.xsd"},
835 : {"urn:oasis:names:tc:ciq:xsdschema:xAL:2.0",
836 : "https://schemas.opengis.net/citygml/xAL/xAL.xsd"},
837 36 : };
838 :
839 2 : bool cityGML2Found = false;
840 38 : for (const auto &[uri, prefix] :
841 40 : topElementParser.GetMapDocNSURIToPrefix())
842 : {
843 19 : if (uri == "http://www.opengis.net/citygml/2.0")
844 1 : cityGML2Found = true;
845 : }
846 :
847 38 : for (const auto &[uri, prefix] :
848 40 : topElementParser.GetMapDocNSURIToPrefix())
849 : {
850 19 : const auto iter = mapWellKnownURIToLocation.find(uri);
851 19 : if (iter != mapWellKnownURIToLocation.end())
852 : {
853 15 : aoXSDs.push_back(PairURIFilename(uri, iter->second));
854 : }
855 4 : else if (cityGML2Found && uri == "http://www.opengis.net/gml")
856 : {
857 1 : aoXSDs.push_back(PairURIFilename(
858 : uri,
859 : "https://schemas.opengis.net/gml/3.1.1/base/gml.xsd"));
860 : }
861 3 : else if (uri != "http://www.w3.org/2001/XMLSchema-instance")
862 : {
863 2 : CPLDebug("GMLAS",
864 : "Could not find schema location for %s(%s)",
865 : prefix.c_str(), uri.c_str());
866 : }
867 : }
868 :
869 2 : m_aoXSDsManuallyPassed = aoXSDs;
870 : }
871 : }
872 : else
873 : {
874 35 : aoXSDs = BuildXSDVector(osXSDFilenames);
875 : }
876 190 : if (fpGML)
877 : {
878 : m_osHash =
879 158 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "HASH", "");
880 158 : if (m_osHash.empty())
881 : {
882 152 : fpGML->Seek(0, SEEK_SET);
883 304 : std::string osBuffer;
884 152 : osBuffer.resize(8192);
885 152 : size_t nRead = fpGML->Read(&osBuffer[0], 1, 8192);
886 152 : osBuffer.resize(nRead);
887 152 : size_t nPos = osBuffer.find("timeStamp=\"");
888 152 : if (nPos != std::string::npos)
889 : {
890 : size_t nPos2 =
891 6 : osBuffer.find('"', nPos + strlen("timeStamp=\""));
892 6 : if (nPos2 != std::string::npos)
893 6 : osBuffer.replace(nPos, nPos2 - nPos + 1, nPos2 - nPos + 1,
894 6 : ' ');
895 : }
896 : CPL_SHA256Context ctxt;
897 152 : CPL_SHA256Init(&ctxt);
898 152 : CPL_SHA256Update(&ctxt, osBuffer.data(), osBuffer.size());
899 :
900 : VSIStatBufL sStat;
901 152 : if (VSIStatL(m_osGMLFilename, &sStat) == 0)
902 : {
903 152 : m_nFileSize = sStat.st_size;
904 152 : GUInt64 nFileSizeLittleEndian =
905 152 : static_cast<GUInt64>(sStat.st_size);
906 152 : CPL_LSBPTR64(&nFileSizeLittleEndian);
907 152 : CPL_SHA256Update(&ctxt, &nFileSizeLittleEndian,
908 : sizeof(nFileSizeLittleEndian));
909 : }
910 :
911 : GByte abyHash[CPL_SHA256_HASH_SIZE];
912 152 : CPL_SHA256Final(&ctxt, abyHash);
913 : // Half of the hash should be enough for our purpose
914 152 : char *pszHash = CPLBinaryToHex(CPL_SHA256_HASH_SIZE / 2, abyHash);
915 152 : m_osHash = pszHash;
916 152 : CPLFree(pszHash);
917 : }
918 :
919 158 : fpGML->Seek(0, SEEK_SET);
920 158 : PushUnusedGMLFilePointer(fpGML);
921 : }
922 :
923 190 : if (aoXSDs.empty())
924 : {
925 1 : if (osXSDFilenames.empty())
926 : {
927 1 : CPLError(CE_Failure, CPLE_AppDefined,
928 : "No schema locations found when analyzing data file: "
929 : "%s open option must be provided",
930 : szXSD_OPTION);
931 : }
932 : else
933 : {
934 0 : CPLError(CE_Failure, CPLE_AppDefined, "No schema locations found");
935 : }
936 1 : return false;
937 : }
938 :
939 378 : m_bSchemaFullChecking = CPLFetchBool(poOpenInfo->papszOpenOptions,
940 : szSCHEMA_FULL_CHECKING_OPTION,
941 189 : m_oConf.m_bSchemaFullChecking);
942 :
943 378 : m_bHandleMultipleImports = CPLFetchBool(poOpenInfo->papszOpenOptions,
944 : szHANDLE_MULTIPLE_IMPORTS_OPTION,
945 189 : m_oConf.m_bHandleMultipleImports);
946 :
947 378 : bool bRet = oAnalyzer.Analyze(
948 567 : m_oCache, CPLGetDirnameSafe(m_osGMLFilename).c_str(), aoXSDs,
949 189 : m_bSchemaFullChecking, m_bHandleMultipleImports);
950 189 : if (!bRet)
951 : {
952 11 : return false;
953 : }
954 :
955 178 : if (!osXSDFilenames.empty())
956 31 : m_aoXSDsManuallyPassed = aoXSDs;
957 :
958 178 : m_oMapURIToPrefix = oAnalyzer.GetMapURIToPrefix();
959 :
960 178 : m_osGMLVersionFound = oAnalyzer.GetGMLVersionFound();
961 :
962 178 : const std::set<CPLString> &oSetSchemaURLs = oAnalyzer.GetSchemaURLS();
963 :
964 178 : FillOtherMetadataLayer(poOpenInfo, m_osConfigFile, aoXSDs, oSetSchemaURLs);
965 :
966 178 : if (CPLFetchBool(poOpenInfo->papszOpenOptions,
967 : szEXPOSE_METADATA_LAYERS_OPTION,
968 178 : m_oConf.m_bExposeMetadataLayers))
969 : {
970 15 : m_apoRequestedMetadataLayers.push_back(m_poFieldsMetadataLayer.get());
971 15 : m_apoRequestedMetadataLayers.push_back(m_poLayersMetadataLayer.get());
972 15 : m_apoRequestedMetadataLayers.push_back(m_poRelationshipsLayer.get());
973 15 : m_apoRequestedMetadataLayers.push_back(m_poOtherMetadataLayer.get());
974 : }
975 :
976 356 : const char *pszSwapCoordinates = CSLFetchNameValueDef(
977 178 : poOpenInfo->papszOpenOptions, szSWAP_COORDINATES_OPTION, "AUTO");
978 178 : if (EQUAL(pszSwapCoordinates, "AUTO"))
979 : {
980 176 : m_eSwapCoordinates = GMLAS_SWAP_AUTO;
981 : }
982 2 : else if (CPLTestBool(pszSwapCoordinates))
983 : {
984 1 : m_eSwapCoordinates = GMLAS_SWAP_YES;
985 : }
986 : else
987 : {
988 1 : m_eSwapCoordinates = GMLAS_SWAP_NO;
989 : }
990 :
991 178 : const std::vector<GMLASFeatureClass> &aoClasses = oAnalyzer.GetClasses();
992 :
993 : // First "standard" tables
994 13356 : for (auto &oClass : aoClasses)
995 : {
996 13178 : if (oClass.GetParentXPath().empty())
997 3321 : TranslateClasses(nullptr, oClass);
998 : }
999 : // Then junction tables
1000 13356 : for (auto &oClass : aoClasses)
1001 : {
1002 13178 : if (!oClass.GetParentXPath().empty())
1003 9857 : TranslateClasses(nullptr, oClass);
1004 : }
1005 :
1006 : // And now do initialization since we need to have instantiated everything
1007 : // to be able to do cross-layer links
1008 17136 : for (auto &poLayer : m_apoLayers)
1009 : {
1010 16958 : poLayer->PostInit(m_oConf.m_bIncludeGeometryXML);
1011 : }
1012 178 : m_bLayerInitFinished = true;
1013 :
1014 : // Do optional validation
1015 356 : m_bValidate = CPLFetchBool(poOpenInfo->papszOpenOptions, szVALIDATE_OPTION,
1016 178 : m_oConf.m_bValidate);
1017 :
1018 356 : m_bRemoveUnusedLayers = CPLFetchBool(poOpenInfo->papszOpenOptions,
1019 : szREMOVE_UNUSED_LAYERS_OPTION,
1020 178 : m_oConf.m_bRemoveUnusedLayers);
1021 :
1022 356 : m_bRemoveUnusedFields = CPLFetchBool(poOpenInfo->papszOpenOptions,
1023 : szREMOVE_UNUSED_FIELDS_OPTION,
1024 178 : m_oConf.m_bRemoveUnusedFields);
1025 :
1026 178 : m_oXLinkResolver.SetConf(m_oConf.m_oXLinkResolution);
1027 178 : m_oXLinkResolver.SetRefreshMode(bRefreshCache);
1028 :
1029 178 : if (m_bValidate || m_bRemoveUnusedLayers ||
1030 126 : (m_bFoundSWE &&
1031 2 : (m_oConf.m_bSWEProcessDataRecord || m_oConf.m_bSWEProcessDataArray)))
1032 : {
1033 54 : CPLErrorReset();
1034 54 : RunFirstPassIfNeeded(nullptr, nullptr, nullptr);
1035 162 : if (CPLFetchBool(poOpenInfo->papszOpenOptions,
1036 : szFAIL_IF_VALIDATION_ERROR_OPTION,
1037 96 : m_oConf.m_bFailIfValidationError) &&
1038 42 : CPLGetLastErrorType() != CE_None)
1039 : {
1040 2 : CPLError(CE_Failure, CPLE_AppDefined,
1041 : "Validation errors encountered");
1042 2 : return false;
1043 : }
1044 : }
1045 176 : if (CPLGetLastErrorType() == CE_Failure)
1046 1 : CPLErrorReset();
1047 :
1048 176 : return true;
1049 : }
1050 :
1051 : /************************************************************************/
1052 : /* TestCapability() */
1053 : /************************************************************************/
1054 :
1055 96 : int OGRGMLASDataSource::TestCapability(const char *pszCap)
1056 : {
1057 96 : return EQUAL(pszCap, ODsCRandomLayerRead);
1058 : }
1059 :
1060 : /************************************************************************/
1061 : /* CreateReader() */
1062 : /************************************************************************/
1063 :
1064 : GMLASReader *
1065 1282 : OGRGMLASDataSource::CreateReader(std::shared_ptr<VSIVirtualHandle> &fpGML,
1066 : GDALProgressFunc pfnProgress,
1067 : void *pProgressData)
1068 : {
1069 1282 : if (fpGML == nullptr)
1070 : {
1071 : // Try recycling an already opened and unused file pointer
1072 1017 : fpGML = PopUnusedGMLFilePointer();
1073 1017 : if (fpGML == nullptr)
1074 7 : fpGML.reset(VSIFOpenL(GetGMLFilename(), "rb"),
1075 : VSIVirtualHandleCloser{});
1076 1017 : if (fpGML == nullptr)
1077 2 : return nullptr;
1078 : }
1079 :
1080 : auto poReader = std::make_unique<GMLASReader>(
1081 2560 : GetCache(), GetIgnoredXPathMatcher(), m_oXLinkResolver);
1082 2560 : poReader->Init(GetGMLFilename(), fpGML, GetMapURIToPrefix(), GetLayers(),
1083 1280 : false, std::vector<PairURIFilename>(), m_bSchemaFullChecking,
1084 1280 : m_bHandleMultipleImports);
1085 :
1086 1280 : poReader->SetSwapCoordinates(GetSwapCoordinates());
1087 :
1088 1280 : poReader->SetFileSize(m_nFileSize);
1089 :
1090 1280 : if (!RunFirstPassIfNeeded(poReader.get(), pfnProgress, pProgressData))
1091 : {
1092 0 : return nullptr;
1093 : }
1094 :
1095 1280 : poReader->SetMapIgnoredXPathToWarn(GetMapIgnoredXPathToWarn());
1096 :
1097 1280 : poReader->SetHash(m_osHash);
1098 :
1099 1280 : return poReader.release();
1100 : }
1101 :
1102 : /************************************************************************/
1103 : /* ResetReading() */
1104 : /************************************************************************/
1105 :
1106 2 : void OGRGMLASDataSource::ResetReading()
1107 : {
1108 2 : m_poReader.reset();
1109 6 : for (auto *poLayer : m_apoRequestedMetadataLayers)
1110 4 : poLayer->ResetReading();
1111 2 : m_bEndOfReaderLayers = false;
1112 2 : m_nCurMetadataLayerIdx = -1;
1113 2 : }
1114 :
1115 : /************************************************************************/
1116 : /* GetNextFeature() */
1117 : /************************************************************************/
1118 :
1119 2774 : OGRFeature *OGRGMLASDataSource::GetNextFeature(OGRLayer **ppoBelongingLayer,
1120 : double *pdfProgressPct,
1121 : GDALProgressFunc pfnProgress,
1122 : void *pProgressData)
1123 : {
1124 2774 : if (m_bEndOfReaderLayers)
1125 : {
1126 3934 : if (m_nCurMetadataLayerIdx >= 0 &&
1127 1963 : m_nCurMetadataLayerIdx <
1128 1963 : static_cast<int>(m_apoRequestedMetadataLayers.size()))
1129 : {
1130 : while (true)
1131 : {
1132 : OGRLayer *poLayer =
1133 2002 : m_apoRequestedMetadataLayers[m_nCurMetadataLayerIdx];
1134 2002 : OGRFeature *poFeature = poLayer->GetNextFeature();
1135 2002 : if (poFeature != nullptr)
1136 : {
1137 1945 : if (pdfProgressPct != nullptr)
1138 0 : *pdfProgressPct = 1.0;
1139 1945 : if (ppoBelongingLayer != nullptr)
1140 1945 : *ppoBelongingLayer = poLayer;
1141 1945 : return poFeature;
1142 : }
1143 57 : if (m_nCurMetadataLayerIdx + 1 <
1144 57 : static_cast<int>(m_apoRequestedMetadataLayers.size()))
1145 : {
1146 39 : m_nCurMetadataLayerIdx++;
1147 : }
1148 : else
1149 : {
1150 18 : m_nCurMetadataLayerIdx = -1;
1151 18 : break;
1152 : }
1153 39 : }
1154 : }
1155 :
1156 26 : if (pdfProgressPct != nullptr)
1157 0 : *pdfProgressPct = 1.0;
1158 26 : if (ppoBelongingLayer != nullptr)
1159 26 : *ppoBelongingLayer = nullptr;
1160 26 : return nullptr;
1161 : }
1162 :
1163 803 : const double dfInitialScanRatio = 0.1;
1164 803 : if (m_poReader == nullptr)
1165 : {
1166 23 : void *pScaledProgress = GDALCreateScaledProgress(
1167 : 0.0, dfInitialScanRatio, pfnProgress, pProgressData);
1168 :
1169 46 : m_poReader.reset(CreateReader(
1170 23 : m_fpGMLParser, pScaledProgress ? GDALScaledProgress : nullptr,
1171 : pScaledProgress));
1172 :
1173 23 : GDALDestroyScaledProgress(pScaledProgress);
1174 :
1175 23 : if (m_poReader == nullptr)
1176 : {
1177 1 : if (pdfProgressPct != nullptr)
1178 0 : *pdfProgressPct = 1.0;
1179 1 : if (ppoBelongingLayer != nullptr)
1180 1 : *ppoBelongingLayer = nullptr;
1181 1 : m_bEndOfReaderLayers = true;
1182 1 : if (!m_apoRequestedMetadataLayers.empty())
1183 : {
1184 1 : m_nCurMetadataLayerIdx = 0;
1185 1 : return GetNextFeature(ppoBelongingLayer, pdfProgressPct,
1186 1 : pfnProgress, pProgressData);
1187 : }
1188 : else
1189 : {
1190 0 : return nullptr;
1191 : }
1192 : }
1193 : }
1194 :
1195 802 : void *pScaledProgress = GDALCreateScaledProgress(
1196 : dfInitialScanRatio, 1.0, pfnProgress, pProgressData);
1197 :
1198 : while (true)
1199 : {
1200 802 : OGRGMLASLayer *poBelongingLayer = nullptr;
1201 : auto poFeature = std::unique_ptr<OGRFeature>(m_poReader->GetNextFeature(
1202 : &poBelongingLayer, pScaledProgress ? GDALScaledProgress : nullptr,
1203 802 : pScaledProgress));
1204 1582 : if (poFeature == nullptr ||
1205 780 : poBelongingLayer->EvaluateFilter(poFeature.get()))
1206 : {
1207 802 : if (ppoBelongingLayer != nullptr)
1208 802 : *ppoBelongingLayer = poBelongingLayer;
1209 802 : if (pdfProgressPct != nullptr)
1210 : {
1211 60 : const vsi_l_offset nOffset = m_fpGMLParser->Tell();
1212 60 : if (nOffset == m_nFileSize)
1213 60 : *pdfProgressPct = 1.0;
1214 : else
1215 0 : *pdfProgressPct =
1216 0 : dfInitialScanRatio +
1217 0 : (1.0 - dfInitialScanRatio) * nOffset / m_nFileSize;
1218 : }
1219 802 : GDALDestroyScaledProgress(pScaledProgress);
1220 802 : if (poFeature == nullptr)
1221 : {
1222 22 : m_bEndOfReaderLayers = true;
1223 22 : if (!m_apoRequestedMetadataLayers.empty())
1224 : {
1225 17 : m_nCurMetadataLayerIdx = 0;
1226 17 : return GetNextFeature(ppoBelongingLayer, pdfProgressPct,
1227 17 : pfnProgress, pProgressData);
1228 : }
1229 : else
1230 : {
1231 5 : return nullptr;
1232 : }
1233 : }
1234 : else
1235 780 : return poFeature.release();
1236 : }
1237 0 : }
1238 : }
1239 :
1240 : /************************************************************************/
1241 : /* GetLayerByXPath() */
1242 : /************************************************************************/
1243 :
1244 37927 : OGRGMLASLayer *OGRGMLASDataSource::GetLayerByXPath(const CPLString &osXPath)
1245 : {
1246 7025100 : for (auto &poLayer : m_apoLayers)
1247 : {
1248 7023940 : if (poLayer->GetFeatureClass().GetXPath() == osXPath)
1249 : {
1250 36764 : return poLayer.get();
1251 : }
1252 : }
1253 1163 : return nullptr;
1254 : }
1255 :
1256 : /************************************************************************/
1257 : /* PushUnusedGMLFilePointer() */
1258 : /************************************************************************/
1259 :
1260 1179 : void OGRGMLASDataSource::PushUnusedGMLFilePointer(
1261 : std::shared_ptr<VSIVirtualHandle> &fpGML)
1262 : {
1263 1179 : if (m_fpGML == nullptr)
1264 : {
1265 1108 : std::swap(m_fpGML, fpGML);
1266 : }
1267 : else
1268 : {
1269 71 : fpGML.reset();
1270 : }
1271 1179 : }
1272 :
1273 : /************************************************************************/
1274 : /* PopUnusedGMLFilePointer() */
1275 : /************************************************************************/
1276 :
1277 1017 : std::shared_ptr<VSIVirtualHandle> OGRGMLASDataSource::PopUnusedGMLFilePointer()
1278 : {
1279 1017 : std::shared_ptr<VSIVirtualHandle> fpGML;
1280 1017 : std::swap(fpGML, m_fpGML);
1281 1017 : return fpGML;
1282 : }
1283 :
1284 : /************************************************************************/
1285 : /* InitReaderWithFirstPassElements() */
1286 : /************************************************************************/
1287 :
1288 1490 : void OGRGMLASDataSource::InitReaderWithFirstPassElements(GMLASReader *poReader)
1289 : {
1290 1490 : if (poReader != nullptr)
1291 : {
1292 1267 : poReader->SetMapSRSNameToInvertedAxis(m_oMapSRSNameToInvertedAxis);
1293 1267 : poReader->SetMapGeomFieldDefnToSRSName(m_oMapGeomFieldDefnToSRSName);
1294 1271 : poReader->SetProcessDataRecord(m_bFoundSWE &&
1295 4 : m_oConf.m_bSWEProcessDataRecord);
1296 1267 : poReader->SetSWEDataArrayLayersRef(m_apoSWEDataArrayLayersRef);
1297 1267 : poReader->SetMapElementIdToLayer(m_oMapElementIdToLayer);
1298 1267 : poReader->SetMapElementIdToPKID(m_oMapElementIdToPKID);
1299 1267 : poReader->SetDefaultSrsDimension(m_nDefaultSrsDimension);
1300 : }
1301 1490 : }
1302 :
1303 : /************************************************************************/
1304 : /* RunFirstPassIfNeeded() */
1305 : /************************************************************************/
1306 :
1307 1510 : bool OGRGMLASDataSource::RunFirstPassIfNeeded(GMLASReader *poReader,
1308 : GDALProgressFunc pfnProgress,
1309 : void *pProgressData)
1310 : {
1311 1510 : if (m_bFirstPassDone)
1312 : {
1313 1378 : InitReaderWithFirstPassElements(poReader);
1314 1378 : return true;
1315 : }
1316 :
1317 132 : m_bFirstPassDone = true;
1318 :
1319 : // Determine if we have geometry fields in any layer
1320 : // If so, do an initial pass to determine the SRS of those geometry fields.
1321 132 : bool bHasGeomFields = false;
1322 14323 : for (auto &poLayer : m_apoLayers)
1323 : {
1324 14212 : poLayer->SetLayerDefnFinalized(true);
1325 14212 : if (poLayer->GetLayerDefn()->GetGeomFieldCount() > 0)
1326 : {
1327 21 : bHasGeomFields = true;
1328 21 : break;
1329 : }
1330 : }
1331 :
1332 132 : bool bSuccess = true;
1333 : const bool bHasURLSpecificRules =
1334 132 : !m_oXLinkResolver.GetConf().m_aoURLSpecificRules.empty();
1335 111 : if (bHasGeomFields || m_bValidate || m_bRemoveUnusedLayers ||
1336 61 : m_bRemoveUnusedFields || bHasURLSpecificRules ||
1337 256 : m_oXLinkResolver.GetConf().m_bResolveInternalXLinks ||
1338 13 : (m_bFoundSWE &&
1339 0 : (m_oConf.m_bSWEProcessDataRecord || m_oConf.m_bSWEProcessDataArray)))
1340 : {
1341 119 : bool bJustOpenedFiled = false;
1342 0 : std::shared_ptr<VSIVirtualHandle> fp;
1343 119 : if (poReader)
1344 43 : fp = poReader->GetFP();
1345 : else
1346 : {
1347 76 : fp.reset(VSIFOpenL(GetGMLFilename(), "rb"),
1348 : VSIVirtualHandleCloser{});
1349 76 : if (fp == nullptr)
1350 : {
1351 7 : return false;
1352 : }
1353 69 : bJustOpenedFiled = true;
1354 : }
1355 :
1356 : auto poReaderFirstPass = std::make_unique<GMLASReader>(
1357 224 : m_oCache, m_oIgnoredXPathMatcher, m_oXLinkResolver);
1358 224 : poReaderFirstPass->Init(GetGMLFilename(), fp, GetMapURIToPrefix(),
1359 112 : GetLayers(), m_bValidate,
1360 112 : m_aoXSDsManuallyPassed, m_bSchemaFullChecking,
1361 112 : m_bHandleMultipleImports);
1362 :
1363 224 : poReaderFirstPass->SetProcessDataRecord(
1364 112 : m_bFoundSWE && m_oConf.m_bSWEProcessDataRecord);
1365 :
1366 112 : poReaderFirstPass->SetFileSize(m_nFileSize);
1367 :
1368 112 : poReaderFirstPass->SetMapIgnoredXPathToWarn(
1369 112 : m_oConf.m_oMapIgnoredXPathToWarn);
1370 :
1371 112 : poReaderFirstPass->SetHash(m_osHash);
1372 :
1373 : // No need to warn afterwards
1374 112 : m_oConf.m_oMapIgnoredXPathToWarn.clear();
1375 :
1376 224 : std::set<CPLString> aoSetRemovedLayerNames;
1377 112 : bSuccess = poReaderFirstPass->RunFirstPass(
1378 112 : pfnProgress, pProgressData, m_bRemoveUnusedLayers,
1379 112 : m_bRemoveUnusedFields,
1380 112 : m_bFoundSWE && m_oConf.m_bSWEProcessDataArray,
1381 : m_poFieldsMetadataLayer.get(), m_poLayersMetadataLayer.get(),
1382 : m_poRelationshipsLayer.get(), aoSetRemovedLayerNames);
1383 :
1384 : std::vector<std::unique_ptr<OGRGMLASLayer>> apoSWEDataArrayLayers =
1385 224 : poReaderFirstPass->StealSWEDataArrayLayersOwned();
1386 115 : for (auto &poLayer : apoSWEDataArrayLayers)
1387 : {
1388 3 : poLayer->SetDataSource(this);
1389 3 : m_apoSWEDataArrayLayersRef.push_back(poLayer.get());
1390 3 : m_apoLayers.emplace_back(std::move(poLayer));
1391 : }
1392 :
1393 : // If we have removed layers, we also need to cleanup our special
1394 : // metadata layers
1395 112 : if (!aoSetRemovedLayerNames.empty())
1396 : {
1397 : // Removing features while iterating works here given the layers
1398 : // are MEM layers
1399 1 : m_poLayersMetadataLayer->ResetReading();
1400 5 : for (auto &poFeature : *m_poLayersMetadataLayer)
1401 : {
1402 : const char *pszLayerName =
1403 4 : poFeature->GetFieldAsString(szLAYER_NAME);
1404 4 : if (aoSetRemovedLayerNames.find(pszLayerName) !=
1405 8 : aoSetRemovedLayerNames.end())
1406 : {
1407 6 : CPL_IGNORE_RET_VAL(m_poLayersMetadataLayer->DeleteFeature(
1408 3 : poFeature->GetFID()));
1409 : }
1410 : }
1411 1 : m_poLayersMetadataLayer->ResetReading();
1412 :
1413 1 : m_poFieldsMetadataLayer->ResetReading();
1414 14 : for (auto &poFeature : *m_poFieldsMetadataLayer)
1415 : {
1416 : const char *pszLayerName =
1417 13 : poFeature->GetFieldAsString(szLAYER_NAME);
1418 : const char *pszRelatedLayerName =
1419 13 : poFeature->GetFieldAsString(szFIELD_RELATED_LAYER);
1420 26 : if (aoSetRemovedLayerNames.find(pszLayerName) !=
1421 47 : aoSetRemovedLayerNames.end() ||
1422 21 : aoSetRemovedLayerNames.find(pszRelatedLayerName) !=
1423 21 : aoSetRemovedLayerNames.end())
1424 : {
1425 12 : CPL_IGNORE_RET_VAL(m_poFieldsMetadataLayer->DeleteFeature(
1426 6 : poFeature->GetFID()));
1427 : }
1428 : }
1429 1 : m_poFieldsMetadataLayer->ResetReading();
1430 :
1431 1 : m_poRelationshipsLayer->ResetReading();
1432 2 : for (auto &poFeature : *m_poRelationshipsLayer)
1433 : {
1434 : const char *pszParentLayerName =
1435 1 : poFeature->GetFieldAsString(szPARENT_LAYER);
1436 : const char *pszChildLayerName =
1437 1 : poFeature->GetFieldAsString(szCHILD_LAYER);
1438 2 : if (aoSetRemovedLayerNames.find(pszParentLayerName) !=
1439 4 : aoSetRemovedLayerNames.end() ||
1440 2 : aoSetRemovedLayerNames.find(pszChildLayerName) !=
1441 2 : aoSetRemovedLayerNames.end())
1442 : {
1443 2 : CPL_IGNORE_RET_VAL(m_poRelationshipsLayer->DeleteFeature(
1444 1 : poFeature->GetFID()));
1445 : }
1446 : }
1447 1 : m_poRelationshipsLayer->ResetReading();
1448 : }
1449 :
1450 : // Store maps to reinject them in real readers
1451 : m_oMapSRSNameToInvertedAxis =
1452 112 : poReaderFirstPass->GetMapSRSNameToInvertedAxis();
1453 : m_oMapGeomFieldDefnToSRSName =
1454 112 : poReaderFirstPass->GetMapGeomFieldDefnToSRSName();
1455 :
1456 112 : m_oMapElementIdToLayer = poReaderFirstPass->GetMapElementIdToLayer();
1457 112 : m_oMapElementIdToPKID = poReaderFirstPass->GetMapElementIdToPKID();
1458 112 : m_nDefaultSrsDimension = poReaderFirstPass->GetDefaultSrsDimension();
1459 :
1460 112 : poReaderFirstPass.reset();
1461 :
1462 112 : fp->Seek(0, SEEK_SET);
1463 112 : if (bJustOpenedFiled)
1464 69 : PushUnusedGMLFilePointer(fp);
1465 :
1466 112 : InitReaderWithFirstPassElements(poReader);
1467 : }
1468 :
1469 125 : return bSuccess;
1470 : }
|