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