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 : #include "ogr_p.h"
17 : #include "ogrlibjsonutils.h"
18 : #include "cpl_time.h"
19 :
20 : #include <algorithm>
21 : #include <cmath>
22 :
23 : namespace GMLAS
24 : {
25 :
26 : /************************************************************************/
27 : /* GMLASWriter */
28 : /************************************************************************/
29 :
30 : typedef std::pair<CPLString, CPLString> PairNSElement;
31 :
32 : typedef std::vector<PairNSElement> XPathComponents;
33 :
34 : typedef std::pair<CPLString, CPLString> PairLayerNameColName;
35 :
36 : class LayerDescription
37 : {
38 : public:
39 : CPLString osName{};
40 : CPLString osXPath{};
41 : CPLString osPKIDName{};
42 : CPLString osParentPKIDName{};
43 : bool bIsSelected = false;
44 : bool bIsTopLevel = false;
45 : bool bIsJunction = false;
46 : // map a field sequential number to a field
47 : std::map<int, GMLASField> oMapIdxToField{};
48 : // map a field xpath to its sequential number
49 : std::map<CPLString, int> oMapFieldXPathToIdx{};
50 : std::map<CPLString, int> oMapFieldNameToOGRIdx{};
51 : std::vector<PairLayerNameColName> aoReferencingLayers{};
52 :
53 : // NOTE: this doesn't scale to arbitrarily large datasets
54 : std::set<GIntBig> aoSetReferencedFIDs{};
55 :
56 117 : LayerDescription() = default;
57 :
58 852 : int GetOGRIdxFromFieldName(const CPLString &osFieldName) const
59 : {
60 852 : const auto oIter = oMapFieldNameToOGRIdx.find(osFieldName);
61 852 : if (oIter == oMapFieldNameToOGRIdx.end())
62 0 : return -1;
63 852 : return oIter->second;
64 : }
65 : };
66 :
67 : class GMLASWriter
68 : {
69 : GMLASConfiguration m_oConf{};
70 : CPLString m_osFilename{};
71 : CPLString m_osGMLVersion{};
72 : CPLString m_osSRSNameFormat{};
73 : #ifdef _WIN32
74 : CPLString m_osEOL = "\r\n";
75 : #else
76 : CPLString m_osEOL = "\n";
77 : #endif
78 : GDALDataset *m_poSrcDS;
79 : CPLStringList m_aosOptions{};
80 : VSIVirtualHandleUniquePtr m_fpXML{};
81 : std::unique_ptr<OGRGMLASDataSource> m_poTmpDS{};
82 : OGRLayer *m_poLayersMDLayer = nullptr;
83 : OGRLayer *m_poFieldsMDLayer = nullptr;
84 : OGRLayer *m_poLayerRelationshipsLayer = nullptr;
85 : std::vector<LayerDescription> m_aoLayerDesc{};
86 : std::map<CPLString, int> m_oMapLayerNameToIdx{};
87 : std::map<CPLString, int> m_oMapXPathToIdx{};
88 : std::map<CPLString, OGRLayer *> m_oMapLayerNameToLayer{};
89 : std::map<CPLString, XPathComponents> m_oMapXPathToComponents{};
90 : std::map<const OGRSpatialReference *, bool> m_oMapSRSToCoordSwap{};
91 :
92 : CPLString m_osTargetNameSpace = szOGRGMLAS_URI;
93 : CPLString m_osTargetNameSpacePrefix = szOGRGMLAS_PREFIX;
94 :
95 : CPLString m_osIndentation = std::string(INDENT_SIZE_DEFAULT, ' ');
96 : int m_nIndentLevel = 0;
97 :
98 674 : void IncIndent()
99 : {
100 674 : ++m_nIndentLevel;
101 674 : }
102 :
103 674 : void DecIndent()
104 : {
105 674 : --m_nIndentLevel;
106 674 : }
107 :
108 : void PrintIndent(VSILFILE *fp);
109 :
110 : void PrintLine(VSILFILE *fp, const char *fmt, ...)
111 : CPL_PRINT_FUNC_FORMAT(3, 4);
112 :
113 : bool WriteXSD(const CPLString &osXSDFilenameIn,
114 : const std::vector<PairURIFilename> &aoXSDs);
115 : bool WriteXMLHeader(bool bWFS2FeatureCollection, GIntBig nTotalFeatures,
116 : bool bGenerateXSD, const CPLString &osXSDFilenameIn,
117 : const std::vector<PairURIFilename> &aoXSDs,
118 : const std::map<CPLString, CPLString> &oMapURIToPrefix);
119 : bool CollectLayers();
120 : bool CollectFields();
121 : bool CollectRelationships();
122 : void ComputeTopLevelFIDs();
123 : bool WriteLayer(bool bWFS2FeatureCollection, const LayerDescription &oDesc,
124 : GIntBig &nFeaturesWritten, GIntBig nTotalTopLevelFeatures,
125 : GDALProgressFunc pfnProgress, void *pProgressData);
126 :
127 : bool WriteFeature(OGRFeature *poFeature, const LayerDescription &oLayerDesc,
128 : const std::set<CPLString> &oSetLayersInIteration,
129 : const XPathComponents &aoInitialComponents,
130 : const XPathComponents &aoPrefixComponents, int nRecLevel);
131 :
132 : void WriteClosingTags(size_t nCommonLength,
133 : const XPathComponents &aoCurComponents,
134 : const XPathComponents &aoNewComponents,
135 : bool bCurIsRegularField, bool bNewIsRegularField);
136 :
137 : void WriteClosingAndStartingTags(const XPathComponents &aoCurComponents,
138 : const XPathComponents &aoNewComponents,
139 : bool bCurIsRegularField);
140 :
141 : void PrintMultipleValuesSeparator(const GMLASField &oField,
142 : const XPathComponents &aoFieldComponents);
143 :
144 : OGRLayer *
145 : GetFilteredLayer(OGRLayer *poSrcLayer, const CPLString &osFilter,
146 : const std::set<CPLString> &oSetLayersInIteration);
147 : void ReleaseFilteredLayer(OGRLayer *poSrcLayer, OGRLayer *poIterLayer);
148 :
149 : bool WriteFieldRegular(OGRFeature *poFeature, const GMLASField &oField,
150 : const LayerDescription &oLayerDesc,
151 : /*XPathComponents& aoLayerComponents,*/
152 : XPathComponents &aoCurComponents,
153 : const XPathComponents &aoPrefixComponents,
154 : /*const std::set<CPLString>& oSetLayersInIteration,*/
155 : bool &bAtLeastOneFieldWritten,
156 : bool &bCurIsRegularField);
157 :
158 : bool WriteFieldNoLink(OGRFeature *poFeature, const GMLASField &oField,
159 : const LayerDescription &oLayerDesc,
160 : XPathComponents &aoLayerComponents,
161 : XPathComponents &aoCurComponents,
162 : const XPathComponents &aoPrefixComponents,
163 : const std::set<CPLString> &oSetLayersInIteration,
164 : int nRecLevel, bool &bAtLeastOneFieldWritten,
165 : bool &bCurIsRegularField);
166 :
167 : bool WriteFieldWithLink(OGRFeature *poFeature, const GMLASField &oField,
168 : const LayerDescription &oLayerDesc,
169 : XPathComponents &aoLayerComponents,
170 : XPathComponents &aoCurComponents,
171 : const XPathComponents &aoPrefixComponents,
172 : const std::set<CPLString> &oSetLayersInIteration,
173 : int nRecLevel, bool &bAtLeastOneFieldWritten,
174 : bool &bCurIsRegularField);
175 :
176 : bool WriteFieldJunctionTable(
177 : OGRFeature *poFeature, const GMLASField &oField,
178 : const LayerDescription &oLayerDesc, XPathComponents &aoLayerComponents,
179 : XPathComponents &aoCurComponents,
180 : const XPathComponents &aoPrefixComponents,
181 : const std::set<CPLString> &oSetLayersInIteration, int nRecLevel,
182 : bool &bAtLeastOneFieldWritten, bool &bCurIsRegularField);
183 :
184 : void Close();
185 :
186 : OGRLayer *GetLayerByName(const CPLString &osName);
187 :
188 : const XPathComponents &SplitXPath(const CPLString &osXPath);
189 :
190 : bool GetCoordSwap(const OGRSpatialReference *poSRS);
191 :
192 : CPL_DISALLOW_COPY_ASSIGN(GMLASWriter)
193 :
194 : public:
195 : GMLASWriter(const char *pszFilename, GDALDataset *poSrcDS,
196 : CSLConstList papszOptions);
197 :
198 : bool Write(GDALProgressFunc pfnProgress, void *pProgressData);
199 : };
200 :
201 : /************************************************************************/
202 : /* GMLASWriter() */
203 : /************************************************************************/
204 :
205 21 : GMLASWriter::GMLASWriter(const char *pszFilename, GDALDataset *poSrcDS,
206 21 : CSLConstList papszOptions)
207 21 : : m_osFilename(pszFilename), m_poSrcDS(poSrcDS), m_aosOptions(papszOptions)
208 : {
209 21 : }
210 :
211 : /************************************************************************/
212 : /* Close() */
213 : /************************************************************************/
214 :
215 10 : void GMLASWriter::Close()
216 : {
217 10 : m_fpXML.reset();
218 10 : m_poTmpDS.reset();
219 10 : }
220 :
221 : /************************************************************************/
222 : /* Write() */
223 : /************************************************************************/
224 :
225 21 : bool GMLASWriter::Write(GDALProgressFunc pfnProgress, void *pProgressData)
226 : {
227 22 : if (m_poSrcDS->GetLayerCount() == 0 &&
228 1 : m_poSrcDS->GetLayerByName(szOGR_OTHER_METADATA) == nullptr)
229 : {
230 1 : CPLError(CE_Failure, CPLE_AppDefined, "Source dataset has no layers");
231 1 : return false;
232 : }
233 :
234 : // Load configuration file
235 : CPLString osConfigFile =
236 40 : m_aosOptions.FetchNameValueDef(szCONFIG_FILE_OPTION, "");
237 20 : bool bUnlinkAfterUse = false;
238 20 : if (osConfigFile.empty())
239 : {
240 19 : osConfigFile = GMLASConfiguration::GetDefaultConfFile(bUnlinkAfterUse);
241 : }
242 20 : if (osConfigFile.empty())
243 : {
244 0 : CPLError(CE_Warning, CPLE_AppDefined,
245 : "No configuration file found. Using hard-coded defaults");
246 0 : m_oConf.Finalize();
247 : }
248 : else
249 : {
250 20 : const bool bOK = m_oConf.Load(osConfigFile);
251 20 : if (bUnlinkAfterUse)
252 0 : VSIUnlink(osConfigFile.c_str());
253 20 : if (!bOK)
254 : {
255 1 : CPLError(CE_Failure, CPLE_AppDefined,
256 : "Loading of configuration failed");
257 1 : return false;
258 : }
259 : }
260 :
261 : CPLString osXSDFilenames =
262 38 : m_aosOptions.FetchNameValueDef(szINPUT_XSD_OPTION, "");
263 38 : std::vector<PairURIFilename> aoXSDs;
264 38 : std::map<CPLString, CPLString> oMapURIToPrefix;
265 38 : CPLString osGMLVersion;
266 :
267 19 : if (!osXSDFilenames.empty())
268 : {
269 : // Create a fake GMLAS dataset from the XSD= value
270 2 : m_poTmpDS = std::make_unique<OGRGMLASDataSource>();
271 2 : GDALOpenInfo oOpenInfo(szGMLAS_PREFIX, GA_ReadOnly);
272 2 : oOpenInfo.papszOpenOptions = CSLSetNameValue(
273 : oOpenInfo.papszOpenOptions, szXSD_OPTION, osXSDFilenames);
274 2 : bool bRet = m_poTmpDS->Open(&oOpenInfo);
275 2 : CSLDestroy(oOpenInfo.papszOpenOptions);
276 2 : oOpenInfo.papszOpenOptions = nullptr;
277 2 : if (!bRet)
278 : {
279 1 : return false;
280 : }
281 : }
282 :
283 18 : GDALDataset *poQueryDS = m_poTmpDS ? m_poTmpDS.get() : m_poSrcDS;
284 :
285 : // No explicit XSD creation option, then we assume that the source
286 : // dataset contains all the metadata layers we need
287 : OGRLayer *poOtherMetadataLayer =
288 18 : poQueryDS->GetLayerByName(szOGR_OTHER_METADATA);
289 18 : if (poOtherMetadataLayer == nullptr)
290 : {
291 1 : CPLError(CE_Failure, CPLE_AppDefined,
292 : "Cannot establish schema since no %s creation option "
293 : "specified and no %s found in source "
294 : "dataset. One of them must be defined.",
295 : szINPUT_XSD_OPTION, szOGR_OTHER_METADATA);
296 1 : return false;
297 : }
298 :
299 17 : m_poLayersMDLayer = poQueryDS->GetLayerByName(szOGR_LAYERS_METADATA);
300 17 : m_poFieldsMDLayer = poQueryDS->GetLayerByName(szOGR_FIELDS_METADATA);
301 17 : m_poLayerRelationshipsLayer =
302 17 : poQueryDS->GetLayerByName(szOGR_LAYER_RELATIONSHIPS);
303 17 : if (m_poLayersMDLayer == nullptr)
304 : {
305 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s not found",
306 : szOGR_LAYERS_METADATA);
307 1 : return false;
308 : }
309 16 : if (m_poFieldsMDLayer == nullptr)
310 : {
311 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s not found",
312 : szOGR_FIELDS_METADATA);
313 1 : return false;
314 : }
315 15 : if (m_poLayerRelationshipsLayer == nullptr)
316 : {
317 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s not found",
318 : szOGR_LAYER_RELATIONSHIPS);
319 1 : return false;
320 : }
321 :
322 28 : std::map<int, CPLString> oMapToUri;
323 28 : std::map<int, CPLString> oMapToLocation;
324 28 : std::map<int, CPLString> oMapToPrefix;
325 129 : for (auto &&poFeature : *poOtherMetadataLayer)
326 : {
327 115 : const char *pszKey = poFeature->GetFieldAsString(szKEY);
328 115 : int i = 0;
329 115 : if (sscanf(pszKey, szNAMESPACE_URI_FMT, &i) == 1 && i > 0)
330 : {
331 26 : oMapToUri[i] = poFeature->GetFieldAsString(szVALUE);
332 : }
333 89 : else if (sscanf(pszKey, szNAMESPACE_LOCATION_FMT, &i) == 1 && i > 0)
334 : {
335 13 : oMapToLocation[i] = poFeature->GetFieldAsString(szVALUE);
336 : }
337 76 : else if (sscanf(pszKey, szNAMESPACE_PREFIX_FMT, &i) == 1 && i > 0)
338 : {
339 26 : oMapToPrefix[i] = poFeature->GetFieldAsString(szVALUE);
340 : }
341 50 : else if (EQUAL(pszKey, szGML_VERSION))
342 : {
343 0 : osGMLVersion = poFeature->GetFieldAsString(szVALUE);
344 : }
345 : }
346 14 : poOtherMetadataLayer->ResetReading();
347 :
348 40 : for (int i = 1; i <= static_cast<int>(oMapToUri.size()); ++i)
349 : {
350 26 : if (oMapToUri.find(i) != oMapToUri.end())
351 : {
352 26 : const CPLString &osURI(oMapToUri[i]);
353 26 : aoXSDs.push_back(PairURIFilename(osURI, oMapToLocation[i]));
354 26 : if (oMapToPrefix.find(i) != oMapToPrefix.end())
355 : {
356 26 : oMapURIToPrefix[osURI] = oMapToPrefix[i];
357 : }
358 : }
359 : }
360 :
361 14 : if (!CollectLayers())
362 1 : return false;
363 :
364 13 : if (!CollectFields())
365 0 : return false;
366 :
367 13 : if (!CollectRelationships())
368 0 : return false;
369 :
370 13 : const char *pszLayers = m_aosOptions.FetchNameValue(szLAYERS_OPTION);
371 13 : if (pszLayers)
372 : {
373 6 : for (const auto &oLayerIter : m_oMapLayerNameToIdx)
374 : {
375 3 : LayerDescription &oDesc = m_aoLayerDesc[oLayerIter.second];
376 3 : oDesc.bIsSelected = false;
377 : }
378 :
379 3 : char **papszLayers = CSLTokenizeString2(pszLayers, ",", 0);
380 5 : for (char **papszIter = papszLayers; *papszIter != nullptr; ++papszIter)
381 : {
382 3 : if (EQUAL(*papszIter, "{SPATIAL_LAYERS}"))
383 : {
384 2 : for (const auto &oLayerIter : m_oMapLayerNameToIdx)
385 : {
386 1 : LayerDescription &oDesc = m_aoLayerDesc[oLayerIter.second];
387 1 : if (oDesc.bIsTopLevel)
388 : {
389 1 : bool bIsGeometric = false;
390 2 : for (const auto &oFieldIter : oDesc.oMapIdxToField)
391 : {
392 2 : if (oFieldIter.second.GetType() ==
393 : GMLAS_FT_GEOMETRY)
394 : {
395 1 : bIsGeometric = true;
396 1 : break;
397 : }
398 : }
399 1 : oDesc.bIsSelected = bIsGeometric;
400 : }
401 : }
402 : }
403 : else
404 : {
405 2 : const auto oLayerIter = m_oMapLayerNameToIdx.find(*papszIter);
406 2 : if (oLayerIter == m_oMapLayerNameToIdx.end())
407 : {
408 1 : CPLError(CE_Warning, CPLE_AppDefined,
409 : "Layer %s specified in LAYERS option "
410 : "does not exist",
411 : *papszIter);
412 1 : CSLDestroy(papszLayers);
413 1 : return false;
414 : }
415 : else
416 : {
417 1 : LayerDescription &oDesc = m_aoLayerDesc[oLayerIter->second];
418 1 : oDesc.bIsSelected = true;
419 : }
420 : }
421 : }
422 2 : CSLDestroy(papszLayers);
423 : }
424 : else
425 : {
426 10 : ComputeTopLevelFIDs();
427 : }
428 :
429 12 : const bool bWFS2FeatureCollection = EQUAL(
430 : m_aosOptions.FetchNameValueDef(szWRAPPING_OPTION, m_oConf.m_osWrapping),
431 : szWFS2_FEATURECOLLECTION);
432 :
433 12 : if (pfnProgress == GDALDummyProgress)
434 12 : pfnProgress = nullptr;
435 : // Compute total number of top level features
436 12 : GIntBig nTotalTopLevelFeatures = -1;
437 12 : if (pfnProgress != nullptr || bWFS2FeatureCollection)
438 : {
439 4 : nTotalTopLevelFeatures = 0;
440 8 : for (const auto &oLayerIter : m_oMapLayerNameToIdx)
441 : {
442 4 : const LayerDescription &oDesc = m_aoLayerDesc[oLayerIter.second];
443 4 : OGRLayer *poSrcLayer = m_poSrcDS->GetLayerByName(oDesc.osName);
444 4 : if (oDesc.bIsSelected && poSrcLayer != nullptr)
445 : {
446 4 : nTotalTopLevelFeatures += poSrcLayer->GetFeatureCount(true);
447 4 : nTotalTopLevelFeatures -=
448 4 : static_cast<GIntBig>(oDesc.aoSetReferencedFIDs.size());
449 : }
450 : }
451 4 : CPLDebug("GMLAS", CPL_FRMT_GIB " top level features to be written",
452 : nTotalTopLevelFeatures);
453 : }
454 :
455 : // Now read options related to writing
456 : int nIndentSize =
457 : std::min(INDENT_SIZE_MAX,
458 : std::max(INDENT_SIZE_MIN,
459 12 : atoi(m_aosOptions.FetchNameValueDef(
460 : szINDENT_SIZE_OPTION,
461 12 : CPLSPrintf("%d", m_oConf.m_nIndentSize)))));
462 12 : m_osIndentation.assign(nIndentSize, ' ');
463 :
464 36 : if (oMapURIToPrefix.find(szGML32_URI) != oMapURIToPrefix.end() ||
465 : // Used by tests
466 24 : oMapURIToPrefix.find("http://fake_gml32") != oMapURIToPrefix.end())
467 : {
468 8 : m_osGMLVersion = "3.2.1";
469 : }
470 : else
471 : {
472 4 : m_osGMLVersion = osGMLVersion;
473 4 : CPL_IGNORE_RET_VAL(osGMLVersion);
474 : }
475 :
476 : m_osSRSNameFormat = m_aosOptions.FetchNameValueDef(
477 12 : szSRSNAME_FORMAT_OPTION, m_oConf.m_osSRSNameFormat);
478 :
479 : CPLString osLineFormat = m_aosOptions.FetchNameValueDef(
480 24 : szLINEFORMAT_OPTION, m_oConf.m_osLineFormat);
481 12 : if (!osLineFormat.empty())
482 : {
483 12 : if (EQUAL(osLineFormat, szCRLF))
484 0 : m_osEOL = "\r\n";
485 12 : else if (EQUAL(osLineFormat, szLF))
486 0 : m_osEOL = "\n";
487 : }
488 :
489 : CPLString osOutXSDFilename =
490 24 : m_aosOptions.FetchNameValueDef(szOUTPUT_XSD_FILENAME_OPTION, "");
491 : const bool bGenerateXSD =
492 20 : !bWFS2FeatureCollection &&
493 20 : (m_osFilename != "/vsistdout/" || !osOutXSDFilename.empty()) &&
494 8 : m_aosOptions.FetchBool(szGENERATE_XSD_OPTION, true);
495 :
496 : // Write .xsd
497 12 : if (bWFS2FeatureCollection)
498 4 : VSIUnlink(CPLResetExtension(m_osFilename, "xsd"));
499 8 : else if (bGenerateXSD && !WriteXSD(osOutXSDFilename, aoXSDs))
500 1 : return false;
501 :
502 : // Write .xml header
503 11 : if (!WriteXMLHeader(bWFS2FeatureCollection, nTotalTopLevelFeatures,
504 : bGenerateXSD, osOutXSDFilename, aoXSDs,
505 : oMapURIToPrefix))
506 1 : return false;
507 :
508 : // Iterate over layers
509 10 : GIntBig nFeaturesWritten = 0;
510 10 : bool bRet = true;
511 124 : for (const auto &oLayerIter : m_oMapLayerNameToIdx)
512 : {
513 114 : if (m_aoLayerDesc[oLayerIter.second].bIsSelected)
514 : {
515 : bRet =
516 30 : WriteLayer(bWFS2FeatureCollection,
517 30 : m_aoLayerDesc[oLayerIter.second], nFeaturesWritten,
518 : nTotalTopLevelFeatures, pfnProgress, pProgressData);
519 30 : if (!bRet)
520 0 : break;
521 : }
522 : }
523 10 : CPLDebug("GMLAS", CPL_FRMT_GIB " top level features written",
524 : nFeaturesWritten);
525 :
526 : // Epilogue of .xml file
527 10 : if (bWFS2FeatureCollection)
528 : {
529 3 : PrintLine(m_fpXML.get(), "</%s:%s>", szWFS_PREFIX,
530 : szFEATURE_COLLECTION);
531 : }
532 : else
533 : {
534 7 : PrintLine(m_fpXML.get(), "</%s:%s>", m_osTargetNameSpacePrefix.c_str(),
535 : szFEATURE_COLLECTION);
536 : }
537 :
538 10 : Close();
539 10 : return bRet;
540 : }
541 :
542 : /************************************************************************/
543 : /* GetLayerByName() */
544 : /************************************************************************/
545 :
546 : // Mostly equivalent to m_poSrcDS->GetLayerByName(), except that we use
547 : // a map to cache instead of linear search.
548 1077 : OGRLayer *GMLASWriter::GetLayerByName(const CPLString &osName)
549 : {
550 1077 : const auto oIter = m_oMapLayerNameToLayer.find(osName);
551 1077 : if (oIter == m_oMapLayerNameToLayer.end())
552 : {
553 117 : OGRLayer *poLayer = m_poSrcDS->GetLayerByName(osName);
554 117 : m_oMapLayerNameToLayer[osName] = poLayer;
555 117 : return poLayer;
556 : }
557 960 : return oIter->second;
558 : }
559 :
560 : /************************************************************************/
561 : /* XMLEscape() */
562 : /************************************************************************/
563 :
564 399 : static CPLString XMLEscape(const CPLString &osStr)
565 : {
566 399 : char *pszEscaped = CPLEscapeString(osStr, -1, CPLES_XML);
567 399 : CPLString osRet(pszEscaped);
568 399 : CPLFree(pszEscaped);
569 399 : return osRet;
570 : }
571 :
572 : /************************************************************************/
573 : /* WriteXSD() */
574 : /************************************************************************/
575 :
576 8 : bool GMLASWriter::WriteXSD(const CPLString &osXSDFilenameIn,
577 : const std::vector<PairURIFilename> &aoXSDs)
578 : {
579 : const CPLString osXSDFilename(
580 8 : !osXSDFilenameIn.empty()
581 : ? osXSDFilenameIn
582 16 : : CPLString(CPLResetExtension(m_osFilename, "xsd")));
583 8 : VSILFILE *fpXSD = VSIFOpenL(osXSDFilename, "wb");
584 8 : if (fpXSD == nullptr)
585 : {
586 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s",
587 : osXSDFilename.c_str());
588 1 : return false;
589 : }
590 :
591 7 : PrintLine(fpXSD, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
592 7 : PrintLine(fpXSD, "<xs:schema ");
593 7 : PrintLine(fpXSD, " targetNamespace=\"%s\"",
594 14 : XMLEscape(m_osTargetNameSpace).c_str());
595 7 : PrintLine(fpXSD, " xmlns:%s=\"%s\"", m_osTargetNameSpacePrefix.c_str(),
596 14 : XMLEscape(m_osTargetNameSpace).c_str());
597 7 : PrintLine(fpXSD, " xmlns:xs=\"%s\"", szXS_URI);
598 7 : PrintLine(fpXSD, " elementFormDefault=\"qualified\" version=\"1.0\" >");
599 :
600 : // Those imports are not really needed, since the schemaLocation are
601 : // already specified in the .xml file, but that helps validating the
602 : // document with libxml2/xmllint since it can only accept one single main
603 : // schema.
604 21 : for (size_t i = 0; i < aoXSDs.size(); ++i)
605 : {
606 14 : if (!aoXSDs[i].second.empty())
607 : {
608 7 : if (!aoXSDs[i].first.empty())
609 : {
610 14 : PrintLine(fpXSD,
611 : "<xs:import namespace=\"%s\" schemaLocation=\"%s\"/>",
612 14 : XMLEscape(aoXSDs[i].first).c_str(),
613 14 : XMLEscape(aoXSDs[i].second).c_str());
614 : }
615 : else
616 : {
617 0 : PrintLine(fpXSD, "<xs:import schemaLocation=\"%s\"/>",
618 0 : XMLEscape(aoXSDs[i].second).c_str());
619 : }
620 : }
621 : }
622 :
623 7 : PrintLine(fpXSD,
624 : "<xs:element name=\"%s\" "
625 : "type=\"%s:%sType\"/>",
626 : szFEATURE_COLLECTION, m_osTargetNameSpacePrefix.c_str(),
627 : szFEATURE_COLLECTION);
628 :
629 7 : PrintLine(fpXSD, "<xs:complexType name=\"%sType\">", szFEATURE_COLLECTION);
630 7 : PrintLine(fpXSD, " <xs:sequence>");
631 7 : PrintLine(fpXSD,
632 : " <xs:element name=\"%s\" "
633 : "minOccurs=\"0\" maxOccurs=\"unbounded\">",
634 : szFEATURE_MEMBER);
635 7 : PrintLine(fpXSD, " <xs:complexType>");
636 7 : PrintLine(fpXSD, " <xs:sequence>");
637 7 : PrintLine(fpXSD, " <xs:any/>");
638 7 : PrintLine(fpXSD, " </xs:sequence>");
639 7 : PrintLine(fpXSD, " </xs:complexType>");
640 7 : PrintLine(fpXSD, " </xs:element>");
641 7 : PrintLine(fpXSD, " </xs:sequence>");
642 7 : PrintLine(fpXSD, "</xs:complexType>");
643 7 : PrintLine(fpXSD, "</xs:schema>");
644 :
645 7 : VSIFCloseL(fpXSD);
646 :
647 7 : return true;
648 : }
649 :
650 : /************************************************************************/
651 : /* WriteXMLHeader() */
652 : /************************************************************************/
653 :
654 11 : bool GMLASWriter::WriteXMLHeader(
655 : bool bWFS2FeatureCollection, GIntBig nTotalFeatures, bool bGenerateXSD,
656 : const CPLString &osXSDFilenameIn,
657 : const std::vector<PairURIFilename> &aoXSDs,
658 : const std::map<CPLString, CPLString> &oMapURIToPrefix)
659 : {
660 11 : m_fpXML.reset(VSIFOpenL(m_osFilename, "wb"));
661 11 : if (m_fpXML == nullptr)
662 : {
663 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s",
664 : m_osFilename.c_str());
665 1 : return false;
666 : }
667 :
668 : // Delete potentially existing .gfs file
669 10 : VSIUnlink(CPLResetExtension(m_osFilename, "gfs"));
670 :
671 20 : std::map<CPLString, CPLString> aoWrittenPrefixes;
672 10 : aoWrittenPrefixes[szXSI_PREFIX] = szXSI_URI;
673 :
674 10 : PrintLine(m_fpXML.get(), "<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
675 10 : if (bWFS2FeatureCollection)
676 : {
677 3 : PrintLine(m_fpXML.get(), "<%s:%s", szWFS_PREFIX, szFEATURE_COLLECTION);
678 :
679 : const CPLString osTimestamp(m_aosOptions.FetchNameValueDef(
680 3 : szTIMESTAMP_OPTION, m_oConf.m_osTimestamp));
681 3 : if (osTimestamp.empty())
682 : {
683 : struct tm sTime;
684 2 : CPLUnixTimeToYMDHMS(time(nullptr), &sTime);
685 2 : PrintLine(m_fpXML.get(),
686 : " timeStamp=\"%04d-%02d-%02dT%02d:%02d:%02dZ\"",
687 2 : sTime.tm_year + 1900, sTime.tm_mon + 1, sTime.tm_mday,
688 : sTime.tm_hour, sTime.tm_min, sTime.tm_sec);
689 : }
690 : else
691 : {
692 1 : PrintLine(m_fpXML.get(), " timeStamp=\"%s\"",
693 : osTimestamp.c_str());
694 : }
695 3 : PrintLine(m_fpXML.get(), " numberMatched=\"unknown\"");
696 3 : PrintLine(m_fpXML.get(), " numberReturned=\"" CPL_FRMT_GIB "\"",
697 : nTotalFeatures);
698 3 : PrintLine(m_fpXML.get(), " xmlns:%s=\"%s\"", szWFS_PREFIX,
699 : szWFS20_URI);
700 3 : aoWrittenPrefixes[szWFS_PREFIX] = szWFS20_URI;
701 : }
702 : else
703 : {
704 7 : PrintLine(m_fpXML.get(), "<%s:%s", m_osTargetNameSpacePrefix.c_str(),
705 : szFEATURE_COLLECTION);
706 7 : PrintLine(m_fpXML.get(), " xmlns:%s=\"%s\"",
707 : m_osTargetNameSpacePrefix.c_str(),
708 14 : XMLEscape(m_osTargetNameSpace).c_str());
709 : }
710 10 : PrintLine(m_fpXML.get(), " xmlns:%s=\"%s\"", szXSI_PREFIX, szXSI_URI);
711 :
712 20 : CPLString osSchemaURI;
713 10 : if (bWFS2FeatureCollection)
714 : {
715 : const CPLString osWFS20SchemaLocation(m_aosOptions.FetchNameValueDef(
716 6 : szWFS20_SCHEMALOCATION_OPTION, m_oConf.m_osWFS20SchemaLocation));
717 3 : osSchemaURI += szWFS20_URI;
718 3 : osSchemaURI += " ";
719 3 : osSchemaURI += osWFS20SchemaLocation;
720 : }
721 7 : else if (bGenerateXSD || !osXSDFilenameIn.empty())
722 : {
723 : const CPLString osXSDFilename(
724 7 : !osXSDFilenameIn.empty()
725 : ? osXSDFilenameIn
726 : : CPLString(
727 14 : CPLGetFilename(CPLResetExtension(m_osFilename, "xsd"))));
728 7 : osSchemaURI += m_osTargetNameSpace;
729 7 : osSchemaURI += " ";
730 7 : osSchemaURI += osXSDFilename;
731 : }
732 :
733 30 : for (size_t i = 0; i < aoXSDs.size(); ++i)
734 : {
735 20 : const CPLString &osURI(aoXSDs[i].first);
736 20 : const CPLString &osLocation(aoXSDs[i].second);
737 :
738 20 : CPLString osPrefix;
739 20 : if (!osURI.empty())
740 : {
741 20 : const auto oIter = oMapURIToPrefix.find(osURI);
742 20 : if (oIter != oMapURIToPrefix.end())
743 : {
744 20 : osPrefix = oIter->second;
745 : }
746 : }
747 20 : if (!osPrefix.empty())
748 : {
749 20 : const auto &oIter = aoWrittenPrefixes.find(osPrefix);
750 20 : if (oIter != aoWrittenPrefixes.end())
751 : {
752 0 : if (oIter->second != osURI)
753 : {
754 0 : CPLDebug("GMLAS",
755 : "Namespace prefix %s already defined as URI %s "
756 : "but now redefefined as %s. Skipped",
757 0 : osPrefix.c_str(), oIter->second.c_str(),
758 : osURI.c_str());
759 : }
760 0 : continue;
761 : }
762 20 : aoWrittenPrefixes[osPrefix] = osURI;
763 : }
764 :
765 20 : if (osURI.empty())
766 : {
767 0 : if (!osLocation.empty())
768 : {
769 0 : PrintLine(m_fpXML.get(), " xsi:%s=\"%s\"",
770 : szNO_NAMESPACE_SCHEMA_LOCATION,
771 0 : XMLEscape(osLocation).c_str());
772 : }
773 : }
774 : else
775 : {
776 20 : if (osPrefix.empty())
777 : {
778 0 : osPrefix = CPLSPrintf("ns%d", static_cast<int>(i));
779 : }
780 :
781 20 : PrintLine(m_fpXML.get(), " xmlns:%s=\"%s\"", osPrefix.c_str(),
782 40 : XMLEscape(osURI).c_str());
783 :
784 20 : if (!osLocation.empty())
785 : {
786 10 : if (!osSchemaURI.empty())
787 10 : osSchemaURI += " ";
788 10 : osSchemaURI += osURI;
789 10 : osSchemaURI += " ";
790 10 : osSchemaURI += osLocation;
791 : }
792 : }
793 : }
794 :
795 10 : if (!osSchemaURI.empty())
796 : {
797 10 : PrintLine(m_fpXML.get(), " xsi:%s=\"%s\" >", szSCHEMA_LOCATION,
798 20 : XMLEscape(osSchemaURI).c_str());
799 : }
800 :
801 : // Write optional user comment
802 : CPLString osComment(
803 10 : m_aosOptions.FetchNameValueDef(szCOMMENT_OPTION, m_oConf.m_osComment));
804 10 : if (!osComment.empty())
805 : {
806 : while (true)
807 : {
808 3 : const size_t nSizeBefore = osComment.size();
809 3 : osComment.replaceAll("--", "- -");
810 3 : if (nSizeBefore == osComment.size())
811 1 : break;
812 2 : }
813 1 : PrintLine(m_fpXML.get(), "<!-- %s -->", osComment.c_str());
814 : }
815 :
816 10 : return true;
817 : }
818 :
819 : /************************************************************************/
820 : /* CollectLayers() */
821 : /************************************************************************/
822 :
823 14 : bool GMLASWriter::CollectLayers()
824 : {
825 14 : OGRFeatureDefn *poFDefn = m_poLayersMDLayer->GetLayerDefn();
826 14 : const char *const apszFields[] = {szLAYER_NAME, szLAYER_XPATH,
827 : szLAYER_CATEGORY, szLAYER_PKID_NAME,
828 : szLAYER_PARENT_PKID_NAME};
829 79 : for (size_t i = 0; i < CPL_ARRAYSIZE(apszFields); ++i)
830 : {
831 66 : if (poFDefn->GetFieldIndex(apszFields[i]) < 0)
832 : {
833 2 : CPLError(CE_Failure, CPLE_AppDefined,
834 1 : "Cannot find field %s in %s layer", apszFields[i],
835 1 : m_poLayersMDLayer->GetName());
836 1 : return false;
837 : }
838 : }
839 :
840 13 : m_poLayersMDLayer->SetAttributeFilter(nullptr);
841 13 : m_poLayersMDLayer->ResetReading();
842 130 : for (auto &&poFeature : *m_poLayersMDLayer)
843 : {
844 117 : LayerDescription desc;
845 117 : desc.osName = poFeature->GetFieldAsString(szLAYER_NAME);
846 117 : desc.osXPath = poFeature->GetFieldAsString(szLAYER_XPATH);
847 117 : desc.osPKIDName = poFeature->GetFieldAsString(szLAYER_PKID_NAME);
848 : desc.osParentPKIDName =
849 117 : poFeature->GetFieldAsString(szLAYER_PARENT_PKID_NAME);
850 117 : desc.bIsTopLevel = EQUAL(poFeature->GetFieldAsString(szLAYER_CATEGORY),
851 : szTOP_LEVEL_ELEMENT);
852 117 : desc.bIsSelected = desc.bIsTopLevel;
853 117 : desc.bIsJunction = EQUAL(poFeature->GetFieldAsString(szLAYER_CATEGORY),
854 : szJUNCTION_TABLE);
855 :
856 117 : OGRLayer *poLyr = GetLayerByName(desc.osName);
857 117 : if (poLyr)
858 : {
859 117 : if (!desc.osPKIDName.empty())
860 101 : desc.oMapFieldNameToOGRIdx[desc.osPKIDName] =
861 101 : poLyr->GetLayerDefn()->GetFieldIndex(desc.osPKIDName);
862 117 : if (!desc.osParentPKIDName.empty())
863 64 : desc.oMapFieldNameToOGRIdx[desc.osParentPKIDName] =
864 64 : poLyr->GetLayerDefn()->GetFieldIndex(desc.osParentPKIDName);
865 : }
866 :
867 117 : m_aoLayerDesc.push_back(desc);
868 117 : if (m_oMapLayerNameToIdx.find(desc.osName) !=
869 234 : m_oMapLayerNameToIdx.end())
870 : {
871 0 : CPLError(CE_Failure, CPLE_AppDefined,
872 : "Several layers with same %s = %s", szLAYER_NAME,
873 : desc.osName.c_str());
874 0 : return false;
875 : }
876 218 : if (!desc.bIsJunction &&
877 218 : m_oMapXPathToIdx.find(desc.osXPath) != m_oMapXPathToIdx.end())
878 : {
879 0 : CPLError(CE_Failure, CPLE_AppDefined,
880 : "Several layers with same %s = %s", szLAYER_XPATH,
881 : desc.osXPath.c_str());
882 0 : return false;
883 : }
884 117 : const int nIdx = static_cast<int>(m_aoLayerDesc.size() - 1);
885 117 : m_oMapLayerNameToIdx[desc.osName] = nIdx;
886 117 : if (!desc.bIsJunction)
887 101 : m_oMapXPathToIdx[desc.osXPath] = nIdx;
888 : }
889 13 : m_poLayersMDLayer->ResetReading();
890 :
891 13 : return true;
892 : }
893 :
894 : /************************************************************************/
895 : /* CollectFields() */
896 : /************************************************************************/
897 :
898 13 : bool GMLASWriter::CollectFields()
899 : {
900 13 : OGRFeatureDefn *poFDefn = m_poFieldsMDLayer->GetLayerDefn();
901 13 : const char *const apszFields[] = {
902 : szLAYER_NAME, szFIELD_INDEX,
903 : szFIELD_NAME, szFIELD_TYPE,
904 : szFIELD_XPATH, szFIELD_CATEGORY,
905 : szFIELD_RELATED_LAYER, szFIELD_JUNCTION_LAYER,
906 : szFIELD_IS_LIST, szFIELD_MIN_OCCURS,
907 : szFIELD_MAX_OCCURS, szFIELD_REPETITION_ON_SEQUENCE,
908 : szFIELD_DEFAULT_VALUE};
909 182 : for (size_t i = 0; i < CPL_ARRAYSIZE(apszFields); ++i)
910 : {
911 169 : if (poFDefn->GetFieldIndex(apszFields[i]) < 0)
912 : {
913 0 : CPLError(CE_Failure, CPLE_AppDefined,
914 0 : "Cannot find field %s in %s layer", apszFields[i],
915 0 : m_poFieldsMDLayer->GetName());
916 0 : return false;
917 : }
918 : }
919 :
920 13 : m_poFieldsMDLayer->SetAttributeFilter(
921 26 : (CPLString(szFIELD_CATEGORY) + " != '" + szSWE_FIELD + "'").c_str());
922 13 : m_poFieldsMDLayer->ResetReading();
923 677 : for (auto &&poFeature : m_poFieldsMDLayer)
924 : {
925 664 : GMLASField oField;
926 :
927 664 : oField.SetName(poFeature->GetFieldAsString(szFIELD_NAME));
928 :
929 664 : CPLString osLayerName(poFeature->GetFieldAsString(szLAYER_NAME));
930 664 : const auto &oIterToIdx = m_oMapLayerNameToIdx.find(osLayerName);
931 664 : if (oIterToIdx == m_oMapLayerNameToIdx.end())
932 : {
933 : // Shouldn't happen for well behaved metadata
934 0 : CPLError(CE_Warning, CPLE_AppDefined,
935 : "Cannot find in %s layer %s, "
936 : "referenced in %s by field %s",
937 : szOGR_LAYERS_METADATA, osLayerName.c_str(),
938 0 : szOGR_FIELDS_METADATA, oField.GetName().c_str());
939 0 : continue;
940 : }
941 664 : if (m_aoLayerDesc[oIterToIdx->second].bIsJunction)
942 : {
943 0 : continue;
944 : }
945 :
946 664 : CPLString osXPath(poFeature->GetFieldAsString(szFIELD_XPATH));
947 664 : oField.SetXPath(osXPath);
948 :
949 664 : CPLString osType(poFeature->GetFieldAsString(szFIELD_TYPE));
950 664 : if (!osType.empty())
951 : {
952 584 : if (osType == szFAKEXS_JSON_DICT)
953 20 : oField.SetType(GMLAS_FT_STRING, osType);
954 564 : else if (osType == szFAKEXS_GEOMETRY)
955 : {
956 135 : oField.SetType(GMLAS_FT_GEOMETRY, osType);
957 : // Hack for geometry field that have a xpath like
958 : // foo/bar/gml:Point,foo/bar/gml:LineString,...
959 135 : size_t nPos = osXPath.find("/gml:Point,");
960 135 : if (nPos != std::string::npos)
961 0 : osXPath.resize(nPos);
962 135 : oField.SetXPath(osXPath);
963 : }
964 : else
965 429 : oField.SetType(GMLASField::GetTypeFromString(osType), osType);
966 : }
967 :
968 664 : CPLString osCategory(poFeature->GetFieldAsString(szFIELD_CATEGORY));
969 664 : if (osCategory == szREGULAR)
970 : {
971 568 : oField.SetCategory(GMLASField::REGULAR);
972 : }
973 96 : else if (osCategory == szPATH_TO_CHILD_ELEMENT_NO_LINK)
974 : {
975 56 : oField.SetCategory(GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
976 : }
977 40 : else if (osCategory == szPATH_TO_CHILD_ELEMENT_WITH_LINK)
978 : {
979 16 : oField.SetCategory(GMLASField::PATH_TO_CHILD_ELEMENT_WITH_LINK);
980 : }
981 24 : else if (osCategory == szPATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE)
982 : {
983 16 : oField.SetCategory(
984 : GMLASField::PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE);
985 :
986 : CPLString osJunctionLayer(
987 16 : poFeature->GetFieldAsString(szFIELD_JUNCTION_LAYER));
988 16 : if (osJunctionLayer.empty())
989 : {
990 : // Shouldn't happen for well behaved metadata
991 0 : CPLError(CE_Warning, CPLE_AppDefined,
992 : "Missing value for %s for field (%s,%s)",
993 : szFIELD_JUNCTION_LAYER, osLayerName.c_str(),
994 0 : oField.GetName().c_str());
995 0 : continue;
996 : }
997 16 : oField.SetJunctionLayer(osJunctionLayer);
998 : }
999 8 : else if (osCategory == szGROUP)
1000 : {
1001 8 : oField.SetCategory(GMLASField::GROUP);
1002 : }
1003 : else
1004 : {
1005 : // Shouldn't happen for well behaved metadata
1006 0 : CPLError(CE_Warning, CPLE_AppDefined,
1007 : "Unknown category = %s for field (%s,%s)",
1008 : osCategory.c_str(), osLayerName.c_str(),
1009 0 : oField.GetName().c_str());
1010 0 : continue;
1011 : }
1012 :
1013 : CPLString osRelatedLayer(
1014 664 : poFeature->GetFieldAsString(szFIELD_RELATED_LAYER));
1015 760 : if (!osRelatedLayer.empty() &&
1016 96 : m_oMapLayerNameToIdx.find(osRelatedLayer) !=
1017 760 : m_oMapLayerNameToIdx.end())
1018 : {
1019 96 : oField.SetRelatedClassXPath(
1020 96 : m_aoLayerDesc[m_oMapLayerNameToIdx[osRelatedLayer]].osXPath);
1021 : }
1022 :
1023 664 : oField.SetList(
1024 664 : CPL_TO_BOOL(poFeature->GetFieldAsInteger(szFIELD_IS_LIST)));
1025 :
1026 664 : oField.SetMinOccurs(poFeature->GetFieldAsInteger(szFIELD_MIN_OCCURS));
1027 664 : oField.SetMaxOccurs(poFeature->GetFieldAsInteger(szFIELD_MAX_OCCURS));
1028 664 : oField.SetRepetitionOnSequence(CPL_TO_BOOL(
1029 : poFeature->GetFieldAsInteger(szFIELD_REPETITION_ON_SEQUENCE)));
1030 664 : oField.SetDefaultValue(
1031 : poFeature->GetFieldAsString(szFIELD_DEFAULT_VALUE));
1032 :
1033 664 : const int nIdx = poFeature->GetFieldAsInteger(szFIELD_INDEX);
1034 :
1035 664 : const int nLayerIdx = m_oMapLayerNameToIdx[osLayerName];
1036 664 : LayerDescription &oLayerDesc = m_aoLayerDesc[nLayerIdx];
1037 664 : if (oLayerDesc.oMapIdxToField.find(nIdx) !=
1038 1328 : oLayerDesc.oMapIdxToField.end())
1039 : {
1040 : // Shouldn't happen for well behaved metadata
1041 0 : CPLError(CE_Failure, CPLE_AppDefined,
1042 : "Field %s of %s has the same index as field %s",
1043 0 : oField.GetName().c_str(), osLayerName.c_str(),
1044 0 : oLayerDesc.oMapIdxToField[nIdx].GetName().c_str());
1045 0 : return false;
1046 : }
1047 664 : oLayerDesc.oMapIdxToField[nIdx] = oField;
1048 :
1049 664 : if (!oField.GetXPath().empty())
1050 : {
1051 656 : if (oLayerDesc.oMapFieldXPathToIdx.find(oField.GetXPath()) !=
1052 1312 : oLayerDesc.oMapFieldXPathToIdx.end())
1053 : {
1054 : // Shouldn't happen for well behaved metadata
1055 0 : CPLError(
1056 : CE_Failure, CPLE_AppDefined,
1057 : "Field %s of %s has the same XPath as field %s",
1058 0 : oField.GetName().c_str(), osLayerName.c_str(),
1059 : oLayerDesc
1060 : .oMapIdxToField
1061 0 : [oLayerDesc.oMapFieldXPathToIdx[oField.GetXPath()]]
1062 0 : .GetName()
1063 : .c_str());
1064 0 : return false;
1065 : }
1066 656 : oLayerDesc.oMapFieldXPathToIdx[oField.GetXPath()] = nIdx;
1067 : }
1068 :
1069 664 : OGRLayer *poLyr = GetLayerByName(osLayerName);
1070 664 : if (poLyr)
1071 : {
1072 664 : oLayerDesc.oMapFieldNameToOGRIdx[oField.GetName()] =
1073 664 : poLyr->GetLayerDefn()->GetFieldIndex(oField.GetName());
1074 664 : if (oField.GetType() == GMLAS_FT_GEOMETRY)
1075 : {
1076 135 : oLayerDesc.oMapFieldNameToOGRIdx[oField.GetName() + "_xml"] =
1077 270 : poLyr->GetLayerDefn()->GetFieldIndex(
1078 270 : (oField.GetName() + "_xml").c_str());
1079 : }
1080 : }
1081 : }
1082 13 : m_poFieldsMDLayer->ResetReading();
1083 :
1084 13 : return true;
1085 : }
1086 :
1087 : /************************************************************************/
1088 : /* CollectRelationships() */
1089 : /************************************************************************/
1090 :
1091 13 : bool GMLASWriter::CollectRelationships()
1092 : {
1093 13 : OGRFeatureDefn *poFDefn = m_poLayerRelationshipsLayer->GetLayerDefn();
1094 13 : const char *const apszFields[] = {szPARENT_LAYER, szCHILD_LAYER,
1095 : szPARENT_ELEMENT_NAME};
1096 52 : for (size_t i = 0; i < CPL_ARRAYSIZE(apszFields); ++i)
1097 : {
1098 39 : if (poFDefn->GetFieldIndex(apszFields[i]) < 0)
1099 : {
1100 0 : CPLError(CE_Failure, CPLE_AppDefined,
1101 0 : "Cannot find field %s in %s layer", apszFields[i],
1102 0 : m_poLayerRelationshipsLayer->GetName());
1103 0 : return false;
1104 : }
1105 : }
1106 :
1107 13 : m_poLayerRelationshipsLayer->SetAttributeFilter(nullptr);
1108 13 : m_poLayerRelationshipsLayer->ResetReading();
1109 :
1110 109 : for (auto &&poFeature : m_poLayerRelationshipsLayer)
1111 : {
1112 : const CPLString osParentLayer(
1113 96 : poFeature->GetFieldAsString(szPARENT_LAYER));
1114 96 : if (m_oMapLayerNameToIdx.find(osParentLayer) ==
1115 192 : m_oMapLayerNameToIdx.end())
1116 : {
1117 : // Shouldn't happen for well behaved metadata
1118 0 : CPLError(CE_Warning, CPLE_AppDefined,
1119 : "Cannot find in %s layer %s, referenced in %s",
1120 : szOGR_LAYERS_METADATA, osParentLayer.c_str(),
1121 : szOGR_LAYER_RELATIONSHIPS);
1122 0 : continue;
1123 : }
1124 :
1125 : const CPLString osChildLayer(
1126 96 : poFeature->GetFieldAsString(szCHILD_LAYER));
1127 96 : if (m_oMapLayerNameToIdx.find(osChildLayer) ==
1128 192 : m_oMapLayerNameToIdx.end())
1129 : {
1130 : // Shouldn't happen for well behaved metadata
1131 0 : CPLError(CE_Warning, CPLE_AppDefined,
1132 : "Cannot find in %s layer %s, referenced in %s",
1133 : szOGR_LAYERS_METADATA, osChildLayer.c_str(),
1134 : szOGR_LAYER_RELATIONSHIPS);
1135 0 : continue;
1136 : }
1137 :
1138 96 : const int nChildLayerIdx = m_oMapLayerNameToIdx[osChildLayer];
1139 96 : if (m_aoLayerDesc[nChildLayerIdx].bIsTopLevel)
1140 : {
1141 : const CPLString osReferencingField(
1142 24 : poFeature->GetFieldAsString(szPARENT_ELEMENT_NAME));
1143 :
1144 48 : m_aoLayerDesc[nChildLayerIdx].aoReferencingLayers.push_back(
1145 48 : PairLayerNameColName(osParentLayer, osReferencingField));
1146 : }
1147 : }
1148 13 : m_poLayerRelationshipsLayer->ResetReading();
1149 :
1150 13 : return true;
1151 : }
1152 :
1153 : /************************************************************************/
1154 : /* ComputeTopLevelFIDs() */
1155 : /* */
1156 : /* Find which features of top-level layers are referenced by other */
1157 : /* features, in which case we don't need to emit them in their layer */
1158 : /************************************************************************/
1159 :
1160 10 : void GMLASWriter::ComputeTopLevelFIDs()
1161 : {
1162 124 : for (size_t i = 0; i < m_aoLayerDesc.size(); ++i)
1163 : {
1164 114 : LayerDescription &oDesc = m_aoLayerDesc[i];
1165 114 : OGRLayer *poLayer = GetLayerByName(oDesc.osName);
1166 144 : if (oDesc.bIsTopLevel && poLayer != nullptr &&
1167 30 : !oDesc.aoReferencingLayers.empty())
1168 : {
1169 44 : for (size_t j = 0; j < oDesc.aoReferencingLayers.size(); ++j)
1170 : {
1171 48 : CPLString osSQL;
1172 48 : CPLString osFID("FID");
1173 48 : if (poLayer->GetFIDColumn() &&
1174 24 : !EQUAL(poLayer->GetFIDColumn(), ""))
1175 : {
1176 24 : osFID = poLayer->GetFIDColumn();
1177 : }
1178 :
1179 : // Determine if the referencing field points to a junction
1180 : // table
1181 : const auto oIter = m_oMapLayerNameToIdx.find(
1182 24 : oDesc.aoReferencingLayers[j].first);
1183 24 : if (oIter != m_oMapLayerNameToIdx.end())
1184 : {
1185 : const LayerDescription &oReferencingLayerDesc =
1186 24 : m_aoLayerDesc[oIter->second];
1187 1212 : for (const auto &oIterField :
1188 2448 : oReferencingLayerDesc.oMapIdxToField)
1189 : {
1190 1236 : const GMLASField &oField = oIterField.second;
1191 1236 : if (oField.GetName() ==
1192 1236 : oDesc.aoReferencingLayers[j].second)
1193 : {
1194 24 : if (oField.GetCategory() ==
1195 : GMLASField::
1196 : PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE)
1197 : {
1198 : osSQL.Printf(
1199 : "SELECT s.\"%s\" AS ogr_main_fid "
1200 : "FROM \"%s\" s "
1201 : "JOIN \"%s\" j ON j.%s = s.\"%s\"",
1202 : osFID.c_str(), oDesc.osName.c_str(),
1203 8 : oField.GetJunctionLayer().c_str(),
1204 16 : szCHILD_PKID, oDesc.osPKIDName.c_str());
1205 : }
1206 24 : break;
1207 : }
1208 : }
1209 : }
1210 :
1211 : // Otherwise we can use the referencing (layer_name,
1212 : // field_name) tuple directly.
1213 24 : if (osSQL.empty())
1214 : {
1215 : osSQL.Printf("SELECT s.\"%s\" AS ogr_main_fid "
1216 : "FROM \"%s\" s "
1217 : "JOIN \"%s\" m ON m.\"%s\" = s.\"%s\"",
1218 : osFID.c_str(), oDesc.osName.c_str(),
1219 16 : oDesc.aoReferencingLayers[j].first.c_str(),
1220 16 : oDesc.aoReferencingLayers[j].second.c_str(),
1221 48 : oDesc.osPKIDName.c_str());
1222 : }
1223 :
1224 24 : CPLDebug("GMLAS", "Executing %s", osSQL.c_str());
1225 : OGRLayer *poSQLLyr =
1226 24 : m_poSrcDS->ExecuteSQL(osSQL, nullptr, nullptr);
1227 24 : if (poSQLLyr)
1228 : {
1229 68 : for (auto &&poFeature : *poSQLLyr)
1230 : {
1231 44 : const GIntBig nFID = poFeature->GetFieldAsInteger64(0);
1232 44 : oDesc.aoSetReferencedFIDs.insert(nFID);
1233 : }
1234 24 : m_poSrcDS->ReleaseResultSet(poSQLLyr);
1235 : }
1236 : }
1237 : }
1238 : }
1239 10 : }
1240 :
1241 : /************************************************************************/
1242 : /* SplitXPath() */
1243 : /************************************************************************/
1244 :
1245 : // Decompose a XPath ns1:foo1/@ns2:foo2/... in its components
1246 : // [ (ns1,foo1), (ns2,@foo2), ... ]
1247 600 : static XPathComponents SplitXPathInternal(const CPLString &osXPath)
1248 : {
1249 600 : char **papszTokens = CSLTokenizeString2(osXPath, "/", 0);
1250 600 : XPathComponents aoComponents;
1251 1946 : for (int i = 0; papszTokens[i] != nullptr; ++i)
1252 : {
1253 1346 : const bool bAttr = (papszTokens[i][0] == '@');
1254 : char **papszNSElt =
1255 1346 : CSLTokenizeString2(papszTokens[i] + (bAttr ? 1 : 0), ":", 0);
1256 1346 : if (papszNSElt[0] != nullptr && papszNSElt[1] != nullptr &&
1257 1238 : papszNSElt[2] == nullptr)
1258 : {
1259 1238 : CPLString osVal(papszNSElt[1]);
1260 1238 : size_t nPos = osVal.find(szEXTRA_SUFFIX);
1261 1238 : if (nPos != std::string::npos)
1262 4 : osVal.resize(nPos);
1263 1238 : aoComponents.push_back(PairNSElement(
1264 2476 : papszNSElt[0], (bAttr ? CPLString("@") : CPLString()) + osVal));
1265 : }
1266 108 : else if (papszNSElt[0] != nullptr && papszNSElt[1] == nullptr)
1267 : {
1268 108 : CPLString osVal(papszNSElt[0]);
1269 108 : size_t nPos = osVal.find(szEXTRA_SUFFIX);
1270 108 : if (nPos != std::string::npos)
1271 0 : osVal.resize(nPos);
1272 108 : aoComponents.push_back(PairNSElement(
1273 216 : "", (bAttr ? CPLString("@") : CPLString()) + osVal));
1274 : }
1275 1346 : CSLDestroy(papszNSElt);
1276 : }
1277 600 : CSLDestroy(papszTokens);
1278 600 : return aoComponents;
1279 : }
1280 :
1281 988 : const XPathComponents &GMLASWriter::SplitXPath(const CPLString &osXPath)
1282 : {
1283 988 : const auto oIter = m_oMapXPathToComponents.find(osXPath);
1284 988 : if (oIter != m_oMapXPathToComponents.end())
1285 388 : return oIter->second;
1286 :
1287 600 : m_oMapXPathToComponents[osXPath] = SplitXPathInternal(osXPath);
1288 600 : return m_oMapXPathToComponents[osXPath];
1289 : }
1290 :
1291 : /************************************************************************/
1292 : /* IsAttr() */
1293 : /************************************************************************/
1294 :
1295 3138 : static bool IsAttr(const PairNSElement &pair)
1296 : {
1297 3138 : return !pair.second.empty() && pair.second[0] == '@';
1298 : }
1299 :
1300 : /************************************************************************/
1301 : /* MakeXPath() */
1302 : /************************************************************************/
1303 :
1304 1462 : static CPLString MakeXPath(const PairNSElement &pair)
1305 : {
1306 1462 : if (pair.first.empty())
1307 : {
1308 64 : if (IsAttr(pair))
1309 128 : return pair.second.substr(1);
1310 : else
1311 0 : return pair.second;
1312 : }
1313 1398 : else if (IsAttr(pair))
1314 36 : return pair.first + ":" + pair.second.substr(1);
1315 : else
1316 2760 : return pair.first + ":" + pair.second;
1317 : }
1318 :
1319 : /************************************************************************/
1320 : /* WriteLayer() */
1321 : /************************************************************************/
1322 :
1323 30 : bool GMLASWriter::WriteLayer(bool bWFS2FeatureCollection,
1324 : const LayerDescription &oDesc,
1325 : GIntBig &nFeaturesWritten,
1326 : GIntBig nTotalTopLevelFeatures,
1327 : GDALProgressFunc pfnProgress, void *pProgressData)
1328 : {
1329 30 : OGRLayer *poSrcLayer = GetLayerByName(oDesc.osName);
1330 30 : if (poSrcLayer == nullptr)
1331 0 : return true;
1332 :
1333 30 : poSrcLayer->ResetReading();
1334 30 : IncIndent();
1335 30 : std::set<CPLString> oSetLayersInIteration;
1336 30 : oSetLayersInIteration.insert(oDesc.osName);
1337 30 : bool bRet = true;
1338 63 : for (auto &&poFeature : *poSrcLayer)
1339 : {
1340 33 : if (oDesc.aoSetReferencedFIDs.find(poFeature->GetFID()) ==
1341 66 : oDesc.aoSetReferencedFIDs.end())
1342 : {
1343 10 : PrintIndent(m_fpXML.get());
1344 10 : if (bWFS2FeatureCollection)
1345 : {
1346 3 : PrintLine(m_fpXML.get(), "<%s:%s>", szWFS_PREFIX, szMEMBER);
1347 : }
1348 : else
1349 : {
1350 7 : PrintLine(m_fpXML.get(), "<%s:%s>",
1351 : m_osTargetNameSpacePrefix.c_str(), szFEATURE_MEMBER);
1352 : }
1353 :
1354 10 : bRet = WriteFeature(poFeature.get(), oDesc, oSetLayersInIteration,
1355 20 : XPathComponents(), XPathComponents(), 0);
1356 :
1357 10 : PrintIndent(m_fpXML.get());
1358 10 : if (bWFS2FeatureCollection)
1359 : {
1360 3 : PrintLine(m_fpXML.get(), "</%s:%s>", szWFS_PREFIX, szMEMBER);
1361 : }
1362 : else
1363 : {
1364 7 : PrintLine(m_fpXML.get(), "</%s:%s>",
1365 : m_osTargetNameSpacePrefix.c_str(), szFEATURE_MEMBER);
1366 : }
1367 :
1368 10 : if (bRet)
1369 : {
1370 10 : nFeaturesWritten++;
1371 10 : const double dfPct = static_cast<double>(nFeaturesWritten) /
1372 : nTotalTopLevelFeatures;
1373 10 : if (pfnProgress && !pfnProgress(dfPct, "", pProgressData))
1374 : {
1375 0 : bRet = false;
1376 : }
1377 : }
1378 10 : if (!bRet)
1379 0 : break;
1380 : }
1381 : }
1382 30 : poSrcLayer->ResetReading();
1383 30 : DecIndent();
1384 :
1385 30 : return bRet;
1386 : }
1387 :
1388 : /************************************************************************/
1389 : /* FindCommonPrefixLength() */
1390 : /************************************************************************/
1391 :
1392 1070 : static size_t FindCommonPrefixLength(const XPathComponents &a,
1393 : const XPathComponents &b)
1394 : {
1395 1070 : size_t i = 0;
1396 2588 : for (; i < a.size() && i < b.size(); ++i)
1397 : {
1398 1980 : if (a[i].first != b[i].first || a[i].second != b[i].second)
1399 462 : break;
1400 : }
1401 1070 : return i;
1402 : }
1403 :
1404 : /************************************************************************/
1405 : /* WriteClosingTags() */
1406 : /************************************************************************/
1407 :
1408 906 : void GMLASWriter::WriteClosingTags(size_t nCommonLength,
1409 : const XPathComponents &aoCurComponents,
1410 : const XPathComponents &aoNewComponents,
1411 : bool bCurIsRegularField,
1412 : bool bNewIsRegularField)
1413 : {
1414 906 : if (nCommonLength < aoCurComponents.size())
1415 : {
1416 608 : bool bFieldIsAnotherAttrOfCurElt = false;
1417 608 : size_t i = aoCurComponents.size() - 1;
1418 :
1419 608 : bool bMustIndent = !bCurIsRegularField;
1420 :
1421 608 : if (IsAttr(aoCurComponents.back()))
1422 : {
1423 160 : if (nCommonLength + 1 == aoCurComponents.size() &&
1424 160 : nCommonLength + 1 == aoNewComponents.size() &&
1425 46 : IsAttr(aoNewComponents.back()))
1426 : {
1427 24 : bFieldIsAnotherAttrOfCurElt = true;
1428 : }
1429 : else
1430 : {
1431 : /*
1432 : a/@b cur
1433 : a new
1434 : ==> <a b="">foo</a>
1435 :
1436 : a/@b cur
1437 : a/c new
1438 : ==> <a b="">
1439 : <c/>
1440 : </a>
1441 :
1442 : a/@b cur
1443 : c new
1444 : ==> <a b=""/>
1445 : <c/>
1446 :
1447 : */
1448 66 : if ((nCommonLength == 0 ||
1449 132 : nCommonLength + 2 <= aoCurComponents.size()) &&
1450 : i >= 2)
1451 : {
1452 20 : PrintLine(m_fpXML.get(), " />");
1453 20 : i -= 2;
1454 20 : DecIndent();
1455 20 : bMustIndent = true;
1456 : }
1457 : else
1458 : {
1459 46 : VSIFPrintfL(m_fpXML.get(), ">");
1460 46 : CPLAssert(i > 0);
1461 46 : i--;
1462 : // Print a new line except in the <elt attr="foo">bar</elt>
1463 : // situation
1464 116 : if (!(nCommonLength + 1 == aoCurComponents.size() &&
1465 46 : nCommonLength == aoNewComponents.size() &&
1466 24 : bNewIsRegularField))
1467 : {
1468 30 : PrintLine(m_fpXML.get(), "%s", "");
1469 : }
1470 : }
1471 : }
1472 : }
1473 :
1474 608 : if (!bFieldIsAnotherAttrOfCurElt)
1475 : {
1476 1178 : for (; i >= nCommonLength; --i)
1477 : {
1478 624 : if (bMustIndent)
1479 : {
1480 150 : PrintIndent(m_fpXML.get());
1481 : }
1482 624 : bMustIndent = true;
1483 624 : PrintLine(m_fpXML.get(), "</%s>",
1484 1248 : MakeXPath(aoCurComponents[i]).c_str());
1485 624 : DecIndent();
1486 624 : if (i == 0)
1487 30 : break;
1488 : }
1489 : }
1490 : }
1491 906 : }
1492 :
1493 : /************************************************************************/
1494 : /* WriteClosingAndStartingTags() */
1495 : /************************************************************************/
1496 :
1497 120 : void GMLASWriter::WriteClosingAndStartingTags(
1498 : const XPathComponents &aoCurComponents,
1499 : const XPathComponents &aoNewComponents, bool bCurIsRegularField)
1500 : {
1501 :
1502 : const size_t nCommonLength =
1503 120 : FindCommonPrefixLength(aoCurComponents, aoNewComponents);
1504 :
1505 120 : WriteClosingTags(nCommonLength, aoCurComponents, aoNewComponents,
1506 : bCurIsRegularField, false);
1507 180 : for (size_t i = nCommonLength; i < aoNewComponents.size(); ++i)
1508 : {
1509 60 : IncIndent();
1510 60 : PrintIndent(m_fpXML.get());
1511 60 : PrintLine(m_fpXML.get(), "<%s>", MakeXPath(aoNewComponents[i]).c_str());
1512 : }
1513 120 : }
1514 :
1515 : /************************************************************************/
1516 : /* WriteFeature() */
1517 : /************************************************************************/
1518 :
1519 222 : bool GMLASWriter::WriteFeature(OGRFeature *poFeature,
1520 : const LayerDescription &oLayerDesc,
1521 : const std::set<CPLString> &oSetLayersInIteration,
1522 : const XPathComponents &aoInitialComponents,
1523 : const XPathComponents &aoPrefixComponents,
1524 : int nRecLevel)
1525 : {
1526 222 : if (nRecLevel == 100)
1527 : {
1528 0 : CPLError(CE_Failure, CPLE_NotSupported,
1529 : "WriteFeature() called with 100 levels of recursion");
1530 0 : return false;
1531 : }
1532 :
1533 444 : XPathComponents aoCurComponents(aoInitialComponents);
1534 444 : XPathComponents aoLayerComponents;
1535 222 : bool bAtLeastOneFieldWritten = false;
1536 222 : bool bCurIsRegularField = false;
1537 1086 : for (const auto &oIter : oLayerDesc.oMapIdxToField)
1538 : {
1539 864 : const GMLASField &oField = oIter.second;
1540 864 : const GMLASField::Category eCategory(oField.GetCategory());
1541 864 : if (eCategory == GMLASField::REGULAR)
1542 : {
1543 728 : WriteFieldRegular(poFeature, oField, oLayerDesc,
1544 : /*aoLayerComponents, */
1545 : aoCurComponents, aoPrefixComponents,
1546 : /*oSetLayersInIteration,*/
1547 : bAtLeastOneFieldWritten, bCurIsRegularField);
1548 : }
1549 136 : else if (eCategory == GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK ||
1550 : eCategory == GMLASField::GROUP)
1551 : {
1552 84 : if (!WriteFieldNoLink(
1553 : poFeature, oField, oLayerDesc, aoLayerComponents,
1554 : aoCurComponents, aoPrefixComponents, oSetLayersInIteration,
1555 : nRecLevel, bAtLeastOneFieldWritten, bCurIsRegularField))
1556 : {
1557 0 : return false;
1558 : }
1559 : }
1560 52 : else if (eCategory == GMLASField::PATH_TO_CHILD_ELEMENT_WITH_LINK)
1561 : {
1562 36 : if (!WriteFieldWithLink(
1563 : poFeature, oField, oLayerDesc, aoLayerComponents,
1564 : aoCurComponents, aoPrefixComponents, oSetLayersInIteration,
1565 : nRecLevel, bAtLeastOneFieldWritten, bCurIsRegularField))
1566 : {
1567 0 : return false;
1568 : }
1569 : }
1570 16 : else if (eCategory ==
1571 : GMLASField::PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE)
1572 : {
1573 16 : if (!WriteFieldJunctionTable(
1574 : poFeature, oField, oLayerDesc, aoLayerComponents,
1575 : aoCurComponents, aoPrefixComponents, oSetLayersInIteration,
1576 : nRecLevel, bAtLeastOneFieldWritten, bCurIsRegularField))
1577 : {
1578 0 : return false;
1579 : }
1580 : }
1581 : }
1582 :
1583 222 : if (!bAtLeastOneFieldWritten && aoInitialComponents.empty() &&
1584 0 : !oLayerDesc.osXPath.empty())
1585 : {
1586 0 : aoLayerComponents = SplitXPath(oLayerDesc.osXPath);
1587 0 : const CPLString osLayerElt(MakeXPath(aoLayerComponents.back()));
1588 0 : PrintIndent(m_fpXML.get());
1589 0 : VSIFPrintfL(m_fpXML.get(), "%s", m_osIndentation.c_str());
1590 0 : PrintLine(m_fpXML.get(), "<%s />", osLayerElt.c_str());
1591 : }
1592 : else
1593 : {
1594 : const size_t nCommonLength =
1595 222 : FindCommonPrefixLength(aoCurComponents, aoInitialComponents);
1596 222 : WriteClosingTags(nCommonLength, aoCurComponents, aoInitialComponents,
1597 : bCurIsRegularField, false);
1598 : }
1599 :
1600 222 : return true;
1601 : }
1602 :
1603 : /************************************************************************/
1604 : /* PrintMultipleValuesSeparator() */
1605 : /************************************************************************/
1606 :
1607 100 : void GMLASWriter::PrintMultipleValuesSeparator(
1608 : const GMLASField &oField, const XPathComponents &aoFieldComponents)
1609 : {
1610 100 : if (oField.IsList())
1611 : {
1612 44 : VSIFPrintfL(m_fpXML.get(), " ");
1613 : }
1614 : else
1615 : {
1616 56 : PrintLine(m_fpXML.get(), "</%s>",
1617 112 : MakeXPath(aoFieldComponents.back()).c_str());
1618 56 : PrintIndent(m_fpXML.get());
1619 56 : VSIFPrintfL(m_fpXML.get(), "<%s>",
1620 112 : MakeXPath(aoFieldComponents.back()).c_str());
1621 : }
1622 100 : }
1623 :
1624 : /************************************************************************/
1625 : /* PrintXMLDouble() */
1626 : /************************************************************************/
1627 :
1628 40 : static void PrintXMLDouble(VSILFILE *fp, double dfVal)
1629 : {
1630 40 : if (std::isinf(dfVal))
1631 : {
1632 0 : if (dfVal > 0)
1633 0 : VSIFPrintfL(fp, "INF");
1634 : else
1635 0 : VSIFPrintfL(fp, "-INF");
1636 : }
1637 40 : else if (std::isnan(dfVal))
1638 0 : VSIFPrintfL(fp, "NaN");
1639 : else
1640 40 : VSIFPrintfL(fp, "%.16g", dfVal);
1641 40 : }
1642 :
1643 : /************************************************************************/
1644 : /* AreGeomsEqualAxisOrderInsensitive() */
1645 : /************************************************************************/
1646 :
1647 13 : static bool AreGeomsEqualAxisOrderInsensitive(const OGRGeometry *poGeomRef,
1648 : OGRGeometry *poGeomModifiable)
1649 : {
1650 13 : if (poGeomRef->Equals(poGeomModifiable))
1651 11 : return true;
1652 2 : poGeomModifiable->swapXY();
1653 2 : return CPL_TO_BOOL(poGeomRef->Equals(poGeomModifiable));
1654 : }
1655 :
1656 : /************************************************************************/
1657 : /* GetCoordSwap() */
1658 : /************************************************************************/
1659 :
1660 0 : bool GMLASWriter::GetCoordSwap(const OGRSpatialReference *poSRS)
1661 : {
1662 0 : const auto oIter = m_oMapSRSToCoordSwap.find(poSRS);
1663 0 : if (oIter != m_oMapSRSToCoordSwap.end())
1664 0 : return oIter->second;
1665 :
1666 0 : bool bCoordSwap = false;
1667 0 : if (m_osSRSNameFormat != "SHORT")
1668 : {
1669 0 : const auto &map = poSRS->GetDataAxisToSRSAxisMapping();
1670 0 : if (map.size() >= 2 && map[0] == 2 && map[1] == 1)
1671 : {
1672 0 : bCoordSwap = true;
1673 : }
1674 : }
1675 0 : m_oMapSRSToCoordSwap[poSRS] = bCoordSwap;
1676 0 : return bCoordSwap;
1677 : }
1678 :
1679 : /************************************************************************/
1680 : /* WriteFieldRegular() */
1681 : /************************************************************************/
1682 :
1683 728 : bool GMLASWriter::WriteFieldRegular(
1684 : OGRFeature *poFeature, const GMLASField &oField,
1685 : const LayerDescription &oLayerDesc,
1686 : /*XPathComponents& aoLayerComponents,*/
1687 : XPathComponents &aoCurComponents, const XPathComponents &aoPrefixComponents,
1688 : /*const std::set<CPLString>& oSetLayersInIteration,*/
1689 : bool &bAtLeastOneFieldWritten, bool &bCurIsRegularField)
1690 : {
1691 728 : const bool bIsGeometryField = oField.GetTypeName() == szFAKEXS_GEOMETRY;
1692 : const int nFieldIdx =
1693 818 : bIsGeometryField ?
1694 : // Some drivers may not store the geometry field name,
1695 : // so for a feature with a single geometry, use it
1696 90 : (poFeature->GetGeomFieldCount() == 1
1697 90 : ? 0
1698 90 : : poFeature->GetGeomFieldIndex(oField.GetName()))
1699 638 : : oLayerDesc.GetOGRIdxFromFieldName(oField.GetName());
1700 1456 : XPathComponents aoFieldComponents = SplitXPath(oField.GetXPath());
1701 728 : aoFieldComponents.insert(aoFieldComponents.begin(),
1702 : aoPrefixComponents.begin(),
1703 1456 : aoPrefixComponents.end());
1704 :
1705 : // For extension/* case
1706 728 : if (!aoFieldComponents.empty() && aoFieldComponents.back().second == "*")
1707 : {
1708 8 : aoFieldComponents.pop_back();
1709 : }
1710 :
1711 : const size_t nCommonLength =
1712 728 : FindCommonPrefixLength(aoCurComponents, aoFieldComponents);
1713 :
1714 : const bool bEmptyContent =
1715 1456 : nFieldIdx < 0 ||
1716 90 : ((bIsGeometryField && !poFeature->GetGeomFieldRef(nFieldIdx)) ||
1717 716 : (!bIsGeometryField && !poFeature->IsFieldSetAndNotNull(nFieldIdx)));
1718 : const bool bIsNull =
1719 728 : m_oConf.m_bUseNullState && (!bIsGeometryField && nFieldIdx >= 0 &&
1720 0 : poFeature->IsFieldNull(nFieldIdx));
1721 728 : bool bMustBeEmittedEvenIfEmpty = oField.GetMinOccurs() > 0 || bIsNull;
1722 728 : if (!m_oConf.m_bUseNullState && oField.GetMinOccurs() == 0 &&
1723 268 : bEmptyContent && nCommonLength + 1 == aoCurComponents.size() &&
1724 116 : IsAttr(aoCurComponents.back()) &&
1725 1464 : nCommonLength == aoFieldComponents.size() &&
1726 732 : oLayerDesc.oMapFieldXPathToIdx.find(oField.GetXPath() + "/" +
1727 736 : szAT_XSI_NIL) ==
1728 732 : oLayerDesc.oMapFieldXPathToIdx.end())
1729 : {
1730 : // This is quite tricky to determine if a <foo bar="baz"/> node is
1731 : // valid or if we must add a xsi:nil="true" to make it valid
1732 : // For now assume that a string can be empty
1733 0 : if (oField.GetType() != GMLAS_FT_STRING)
1734 0 : bMustBeEmittedEvenIfEmpty = true;
1735 : }
1736 :
1737 728 : if (bEmptyContent && !bMustBeEmittedEvenIfEmpty)
1738 160 : return true;
1739 :
1740 : // Do not emit optional attributes at default/fixed value
1741 808 : if (!aoFieldComponents.empty() && oField.GetMinOccurs() == 0 &&
1742 240 : IsAttr(aoFieldComponents.back()))
1743 : {
1744 86 : const CPLString &osDefaultVal(!oField.GetDefaultValue().empty()
1745 86 : ? oField.GetDefaultValue()
1746 78 : : oField.GetFixedValue());
1747 86 : if (!osDefaultVal.empty())
1748 : {
1749 8 : if (oField.GetType() == GMLAS_FT_BOOLEAN)
1750 : {
1751 0 : const int nVal = poFeature->GetFieldAsInteger(nFieldIdx);
1752 0 : if (osDefaultVal == "false" && nVal == 0)
1753 0 : return true;
1754 0 : if (osDefaultVal == "true" && nVal == 1)
1755 0 : return true;
1756 : }
1757 8 : else if (osDefaultVal == poFeature->GetFieldAsString(nFieldIdx))
1758 : {
1759 4 : return true;
1760 : }
1761 : }
1762 : }
1763 :
1764 564 : bAtLeastOneFieldWritten = true;
1765 :
1766 4 : if (bEmptyContent && nCommonLength + 1 == aoCurComponents.size() &&
1767 568 : IsAttr(aoCurComponents.back()) &&
1768 0 : nCommonLength == aoFieldComponents.size())
1769 : {
1770 : // Particular case for <a foo="bar" xsi:nil="true"/>
1771 0 : VSIFPrintfL(m_fpXML.get(), " xsi:nil=\"true\">");
1772 0 : aoCurComponents = aoFieldComponents;
1773 0 : bCurIsRegularField = true;
1774 0 : return true;
1775 : }
1776 : else
1777 : {
1778 : // Emit closing tags
1779 564 : WriteClosingTags(nCommonLength, aoCurComponents, aoFieldComponents,
1780 564 : bCurIsRegularField, true);
1781 : }
1782 :
1783 : // Emit opening tags and attribute names
1784 : // We may do a 0 iteration in case of returning from an attribute
1785 : // to its element
1786 564 : bool bWriteEltContent = true;
1787 1238 : for (size_t i = nCommonLength; i < aoFieldComponents.size(); ++i)
1788 : {
1789 674 : if (i + 1 == aoFieldComponents.size() && IsAttr(aoFieldComponents[i]))
1790 : {
1791 90 : if (aoFieldComponents[i].second != szAT_ANY_ATTR)
1792 : {
1793 82 : VSIFPrintfL(m_fpXML.get(),
1794 164 : " %s=", MakeXPath(aoFieldComponents[i]).c_str());
1795 82 : bWriteEltContent = false;
1796 : }
1797 : }
1798 : else
1799 : {
1800 584 : if (i > nCommonLength)
1801 60 : PrintLine(m_fpXML.get(), "%s", "");
1802 584 : IncIndent();
1803 584 : PrintIndent(m_fpXML.get());
1804 :
1805 710 : if (i + 2 == aoFieldComponents.size() &&
1806 126 : IsAttr(aoFieldComponents[i + 1]))
1807 : {
1808 : // Are we an element that is going to have an
1809 : // attribute ?
1810 66 : VSIFPrintfL(m_fpXML.get(), "<%s",
1811 132 : MakeXPath(aoFieldComponents[i]).c_str());
1812 : }
1813 : else
1814 : {
1815 : // Are we a regular element ?
1816 518 : if (bEmptyContent)
1817 : {
1818 4 : VSIFPrintfL(m_fpXML.get(), "<%s xsi:nil=\"true\">",
1819 8 : MakeXPath(aoFieldComponents[i]).c_str());
1820 : }
1821 : else
1822 : {
1823 514 : VSIFPrintfL(m_fpXML.get(), "<%s>",
1824 1028 : MakeXPath(aoFieldComponents[i]).c_str());
1825 : }
1826 : }
1827 : }
1828 : }
1829 :
1830 : // Write content
1831 564 : if (!bWriteEltContent)
1832 82 : VSIFPrintfL(m_fpXML.get(), "\"");
1833 :
1834 564 : if (!bEmptyContent && oField.GetTypeName() == szFAKEXS_JSON_DICT)
1835 : {
1836 8 : json_object *poObj = nullptr;
1837 8 : if (OGRJSonParse(poFeature->GetFieldAsString(nFieldIdx), &poObj))
1838 : {
1839 8 : if (json_type_object == json_object_get_type(poObj))
1840 : {
1841 : json_object_iter it;
1842 8 : it.key = nullptr;
1843 8 : it.val = nullptr;
1844 8 : it.entry = nullptr;
1845 16 : json_object_object_foreachC(poObj, it)
1846 : {
1847 16 : if (it.val != nullptr &&
1848 8 : json_object_get_type(it.val) == json_type_string)
1849 : {
1850 8 : VSIFPrintfL(
1851 : m_fpXML.get(), " %s=\"%s\"", it.key,
1852 16 : XMLEscape(json_object_get_string(it.val)).c_str());
1853 : }
1854 : }
1855 : }
1856 8 : json_object_put(poObj);
1857 : }
1858 : }
1859 556 : else if (!bEmptyContent && bIsGeometryField)
1860 : {
1861 78 : bool bWriteOGRGeom = true;
1862 78 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(nFieldIdx);
1863 :
1864 : // In case the original GML string was saved, fetch it and compare it
1865 : // to the current OGR geometry. If they match (in a axis order
1866 : // insensitive way), then use the original GML string
1867 : const int nFieldXMLIdx =
1868 78 : oLayerDesc.GetOGRIdxFromFieldName(oField.GetName() + "_xml");
1869 78 : if (nFieldXMLIdx >= 0 && poFeature->IsFieldSetAndNotNull(nFieldXMLIdx))
1870 : {
1871 13 : if (poFeature->GetFieldDefnRef(nFieldXMLIdx)->GetType() ==
1872 : OFTStringList)
1873 : {
1874 1 : if (wkbFlatten(poGeom->getGeometryType()) ==
1875 : wkbGeometryCollection)
1876 : {
1877 2 : OGRGeometryCollection oGC;
1878 : char **papszValues =
1879 1 : poFeature->GetFieldAsStringList(nFieldXMLIdx);
1880 4 : for (int j = 0;
1881 4 : papszValues != nullptr && papszValues[j] != nullptr;
1882 : ++j)
1883 : {
1884 3 : OGRGeometry *poPart = OGRGeometry::FromHandle(
1885 3 : OGR_G_CreateFromGML(papszValues[j]));
1886 3 : if (poPart)
1887 3 : oGC.addGeometryDirectly(poPart);
1888 : }
1889 1 : if (AreGeomsEqualAxisOrderInsensitive(poGeom, &oGC))
1890 : {
1891 4 : for (int j = 0; papszValues != nullptr &&
1892 4 : papszValues[j] != nullptr;
1893 : ++j)
1894 : {
1895 3 : if (j > 0)
1896 2 : PrintMultipleValuesSeparator(oField,
1897 : aoFieldComponents);
1898 3 : VSIFPrintfL(m_fpXML.get(), "%s", papszValues[j]);
1899 : }
1900 1 : bWriteOGRGeom = false;
1901 : }
1902 : }
1903 : }
1904 : else
1905 : {
1906 12 : const char *pszXML = poFeature->GetFieldAsString(nFieldXMLIdx);
1907 : auto poOrigGeom = std::unique_ptr<OGRGeometry>(
1908 24 : OGRGeometry::FromHandle(OGR_G_CreateFromGML(pszXML)));
1909 :
1910 12 : if (poOrigGeom != nullptr)
1911 : {
1912 12 : if (AreGeomsEqualAxisOrderInsensitive(poGeom,
1913 : poOrigGeom.get()))
1914 : {
1915 12 : VSIFPrintfL(m_fpXML.get(), "%s", pszXML);
1916 12 : bWriteOGRGeom = false;
1917 : }
1918 : }
1919 : }
1920 : }
1921 :
1922 78 : if (bWriteOGRGeom)
1923 : {
1924 130 : CPLString osExtraElt;
1925 65 : bool bGMLSurface311 = false;
1926 65 : bool bGMLCurve311 = false;
1927 65 : bool bGMLPoint311 = false;
1928 65 : if (m_osGMLVersion == "3.1.1" &&
1929 65 : MakeXPath(aoFieldComponents.back()) == "gml:Surface")
1930 : {
1931 0 : bGMLSurface311 = true;
1932 : }
1933 65 : else if (m_osGMLVersion == "3.1.1" &&
1934 65 : MakeXPath(aoFieldComponents.back()) == "gml:Curve")
1935 : {
1936 0 : bGMLCurve311 = true;
1937 : }
1938 65 : else if (m_osGMLVersion == "3.1.1" &&
1939 65 : MakeXPath(aoFieldComponents.back()) == "gml:Point")
1940 : {
1941 0 : bGMLPoint311 = true;
1942 : }
1943 :
1944 : const double dfGMLVersion =
1945 65 : m_osGMLVersion.empty() ? 3.2 : CPLAtof(m_osGMLVersion);
1946 65 : char **papszOptions = CSLSetNameValue(
1947 : nullptr, "FORMAT",
1948 65 : (dfGMLVersion >= 2.0 && dfGMLVersion < 3.0) ? "GML2"
1949 65 : : (dfGMLVersion >= 3.0 && dfGMLVersion < 3.2) ? "GML3"
1950 : : "GML32");
1951 65 : papszOptions = CSLSetNameValue(papszOptions, "SRSNAME_FORMAT",
1952 : m_osSRSNameFormat);
1953 :
1954 65 : if (dfGMLVersion < 3.0)
1955 : {
1956 0 : bool bSwap = false;
1957 : const OGRSpatialReference *poSRS =
1958 0 : poGeom->getSpatialReference();
1959 0 : if (poSRS != nullptr && GetCoordSwap(poSRS))
1960 0 : bSwap = true;
1961 0 : papszOptions = CSLSetNameValue(papszOptions, "COORD_SWAP",
1962 : bSwap ? "TRUE" : "FALSE");
1963 : }
1964 :
1965 70 : if (oField.GetMaxOccurs() > 1 &&
1966 5 : wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection)
1967 : {
1968 5 : OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
1969 20 : for (int j = 0; j < poGC->getNumGeometries(); ++j)
1970 : {
1971 15 : if (dfGMLVersion >= 3.2)
1972 : {
1973 : CPLString osGMLID =
1974 15 : poFeature->GetFieldAsString(oLayerDesc.osPKIDName);
1975 15 : osGMLID += CPLSPrintf(".geom%d.%d", nFieldIdx, j);
1976 : papszOptions =
1977 15 : CSLSetNameValue(papszOptions, "GMLID", osGMLID);
1978 : }
1979 15 : if (j > 0)
1980 10 : PrintMultipleValuesSeparator(oField, aoFieldComponents);
1981 15 : char *pszGML = OGR_G_ExportToGMLEx(
1982 : OGRGeometry::ToHandle(poGC->getGeometryRef(j)),
1983 : papszOptions);
1984 15 : if (pszGML)
1985 15 : VSIFPrintfL(m_fpXML.get(), "%s", pszGML);
1986 15 : CPLFree(pszGML);
1987 : }
1988 : }
1989 : else
1990 : {
1991 60 : if (dfGMLVersion >= 3.2)
1992 : {
1993 : CPLString osGMLID =
1994 60 : poFeature->GetFieldAsString(oLayerDesc.osPKIDName);
1995 60 : osGMLID += CPLSPrintf(".geom%d", nFieldIdx);
1996 : papszOptions =
1997 60 : CSLSetNameValue(papszOptions, "GMLID", osGMLID);
1998 : }
1999 60 : char *pszGML = OGR_G_ExportToGMLEx(
2000 : OGRGeometry::ToHandle(poGeom), papszOptions);
2001 60 : if (pszGML)
2002 : {
2003 60 : if (bGMLSurface311 && STARTS_WITH(pszGML, "<gml:Polygon>"))
2004 : {
2005 0 : char *pszEnd = strstr(pszGML, "</gml:Polygon>");
2006 0 : if (pszEnd)
2007 : {
2008 0 : *pszEnd = '\0';
2009 0 : VSIFPrintfL(m_fpXML.get(),
2010 : "<gml:patches><gml:PolygonPatch>%s"
2011 : "</gml:PolygonPatch></gml:patches>",
2012 : pszGML + strlen("<gml:Polygon>"));
2013 0 : }
2014 : }
2015 60 : else if (bGMLCurve311 &&
2016 0 : STARTS_WITH(pszGML, "<gml:LineString>"))
2017 : {
2018 0 : char *pszEnd = strstr(pszGML, "</gml:LineString>");
2019 0 : if (pszEnd)
2020 : {
2021 0 : *pszEnd = '\0';
2022 0 : VSIFPrintfL(
2023 : m_fpXML.get(),
2024 : "<gml:segments><gml:LineStringSegment>%s"
2025 : "</gml:LineStringSegment></gml:segments>",
2026 : pszGML + strlen("<gml:LineString>"));
2027 0 : }
2028 : }
2029 60 : else if (bGMLPoint311 && STARTS_WITH(pszGML, "<gml:Point>"))
2030 : {
2031 0 : char *pszEnd = strstr(pszGML, "</gml:Point>");
2032 0 : if (pszEnd)
2033 : {
2034 0 : *pszEnd = '\0';
2035 0 : VSIFPrintfL(m_fpXML.get(), "%s",
2036 : pszGML + strlen("<gml:Point>"));
2037 0 : }
2038 : }
2039 : else
2040 : {
2041 60 : VSIFPrintfL(m_fpXML.get(), "%s", pszGML);
2042 : }
2043 : }
2044 60 : CPLFree(pszGML);
2045 : }
2046 65 : CSLDestroy(papszOptions);
2047 78 : }
2048 : }
2049 478 : else if (!bEmptyContent && oField.GetTypeName() == szXS_ANY_TYPE)
2050 : {
2051 48 : CPLString osXML(poFeature->GetFieldAsString(nFieldIdx));
2052 : // Check that the content is valid XML
2053 72 : CPLString osValidatingXML("<X>" + osXML + "</X>");
2054 24 : CPLXMLNode *psNode = CPLParseXMLString(osValidatingXML);
2055 24 : if (psNode != nullptr)
2056 : {
2057 24 : VSIFPrintfL(m_fpXML.get(), "%s", osXML.c_str());
2058 24 : CPLDestroyXMLNode(psNode);
2059 : }
2060 : else
2061 : {
2062 : // Otherwise consider it as text and escape
2063 0 : VSIFPrintfL(m_fpXML.get(), "%s", XMLEscape(osXML).c_str());
2064 : }
2065 : }
2066 454 : else if (!bEmptyContent)
2067 : {
2068 : const OGRFieldType eOGRType(
2069 450 : poFeature->GetFieldDefnRef(nFieldIdx)->GetType());
2070 450 : switch (oField.GetType())
2071 : {
2072 20 : case GMLAS_FT_BOOLEAN:
2073 : {
2074 20 : if ((oField.GetMaxOccurs() > 1 || oField.IsList()) &&
2075 : eOGRType == OFTIntegerList)
2076 : {
2077 8 : int nCount = 0;
2078 : const int *panValues =
2079 8 : poFeature->GetFieldAsIntegerList(nFieldIdx, &nCount);
2080 24 : for (int j = 0; j < nCount; ++j)
2081 : {
2082 16 : if (j > 0)
2083 8 : PrintMultipleValuesSeparator(oField,
2084 : aoFieldComponents);
2085 16 : VSIFPrintfL(m_fpXML.get(),
2086 16 : panValues[j] ? "true" : "false");
2087 : }
2088 : }
2089 : else
2090 : {
2091 12 : VSIFPrintfL(m_fpXML.get(),
2092 12 : poFeature->GetFieldAsInteger(nFieldIdx)
2093 : ? "true"
2094 : : "false");
2095 : }
2096 20 : break;
2097 : }
2098 :
2099 76 : case GMLAS_FT_DATETIME:
2100 : case GMLAS_FT_DATE:
2101 : case GMLAS_FT_TIME:
2102 : {
2103 76 : if (eOGRType == OFTDateTime || eOGRType == OFTDate ||
2104 : eOGRType == OFTTime)
2105 : {
2106 : char *pszFormatted =
2107 76 : OGRGetXMLDateTime(poFeature->GetRawFieldRef(nFieldIdx));
2108 76 : char *pszT = strchr(pszFormatted, 'T');
2109 76 : if (oField.GetType() == GMLAS_FT_TIME && pszT != nullptr)
2110 : {
2111 4 : VSIFPrintfL(m_fpXML.get(), "%s", pszT + 1);
2112 : }
2113 : else
2114 : {
2115 72 : if (oField.GetType() == GMLAS_FT_DATE)
2116 : {
2117 4 : if (pszT)
2118 4 : *pszT = '\0';
2119 : }
2120 72 : VSIFPrintfL(m_fpXML.get(), "%s", pszFormatted);
2121 : }
2122 76 : VSIFree(pszFormatted);
2123 : }
2124 : else
2125 : {
2126 0 : CPLError(CE_Warning, CPLE_AppDefined,
2127 : "Invalid content for field %s of type %s: %s",
2128 0 : oField.GetName().c_str(),
2129 0 : oField.GetTypeName().c_str(),
2130 : poFeature->GetFieldAsString(nFieldIdx));
2131 : }
2132 76 : break;
2133 : }
2134 :
2135 4 : case GMLAS_FT_BASE64BINARY:
2136 : {
2137 4 : if (eOGRType == OFTBinary)
2138 : {
2139 4 : int nCount = 0;
2140 : GByte *pabyContent =
2141 4 : poFeature->GetFieldAsBinary(nFieldIdx, &nCount);
2142 4 : char *pszBase64 = CPLBase64Encode(nCount, pabyContent);
2143 4 : VSIFPrintfL(m_fpXML.get(), "%s", pszBase64);
2144 4 : CPLFree(pszBase64);
2145 : }
2146 : else
2147 : {
2148 0 : CPLError(CE_Warning, CPLE_AppDefined,
2149 : "Invalid content for field %s of type %s: %s",
2150 0 : oField.GetName().c_str(),
2151 0 : oField.GetTypeName().c_str(),
2152 : poFeature->GetFieldAsString(nFieldIdx));
2153 : }
2154 4 : break;
2155 : }
2156 :
2157 4 : case GMLAS_FT_HEXBINARY:
2158 : {
2159 4 : if (eOGRType == OFTBinary)
2160 : {
2161 4 : int nCount = 0;
2162 : GByte *pabyContent =
2163 4 : poFeature->GetFieldAsBinary(nFieldIdx, &nCount);
2164 40 : for (int i = 0; i < nCount; ++i)
2165 36 : VSIFPrintfL(m_fpXML.get(), "%02X", pabyContent[i]);
2166 : }
2167 : else
2168 : {
2169 0 : CPLError(CE_Warning, CPLE_AppDefined,
2170 : "Invalid content for field %s of type %s: %s",
2171 0 : oField.GetName().c_str(),
2172 0 : oField.GetTypeName().c_str(),
2173 : poFeature->GetFieldAsString(nFieldIdx));
2174 : }
2175 4 : break;
2176 : }
2177 :
2178 346 : default:
2179 : {
2180 382 : if ((oField.GetMaxOccurs() > 1 || oField.IsList()) &&
2181 36 : (eOGRType == OFTStringList || eOGRType == OFTRealList ||
2182 8 : eOGRType == OFTIntegerList ||
2183 : eOGRType == OFTInteger64List))
2184 : {
2185 64 : if (eOGRType == OFTStringList)
2186 : {
2187 : char **papszValues =
2188 28 : poFeature->GetFieldAsStringList(nFieldIdx);
2189 84 : for (int j = 0; papszValues != nullptr &&
2190 84 : papszValues[j] != nullptr;
2191 : ++j)
2192 : {
2193 56 : if (j > 0)
2194 28 : PrintMultipleValuesSeparator(oField,
2195 : aoFieldComponents);
2196 56 : VSIFPrintfL(m_fpXML.get(), "%s",
2197 112 : XMLEscape(papszValues[j]).c_str());
2198 : }
2199 : }
2200 36 : else if (eOGRType == OFTRealList)
2201 : {
2202 12 : int nCount = 0;
2203 : const double *padfValues =
2204 12 : poFeature->GetFieldAsDoubleList(nFieldIdx, &nCount);
2205 40 : for (int j = 0; j < nCount; ++j)
2206 : {
2207 28 : if (j > 0)
2208 16 : PrintMultipleValuesSeparator(oField,
2209 : aoFieldComponents);
2210 28 : PrintXMLDouble(m_fpXML.get(), padfValues[j]);
2211 : }
2212 : }
2213 24 : else if (eOGRType == OFTIntegerList)
2214 : {
2215 16 : int nCount = 0;
2216 16 : const int *panValues = poFeature->GetFieldAsIntegerList(
2217 : nFieldIdx, &nCount);
2218 56 : for (int j = 0; j < nCount; ++j)
2219 : {
2220 40 : if (j > 0)
2221 24 : PrintMultipleValuesSeparator(oField,
2222 : aoFieldComponents);
2223 40 : VSIFPrintfL(m_fpXML.get(), "%d", panValues[j]);
2224 : }
2225 : }
2226 8 : else if (eOGRType == OFTInteger64List)
2227 : {
2228 8 : int nCount = 0;
2229 : const GIntBig *panValues =
2230 8 : poFeature->GetFieldAsInteger64List(nFieldIdx,
2231 : &nCount);
2232 28 : for (int j = 0; j < nCount; ++j)
2233 : {
2234 20 : if (j > 0)
2235 12 : PrintMultipleValuesSeparator(oField,
2236 : aoFieldComponents);
2237 20 : VSIFPrintfL(m_fpXML.get(), CPL_FRMT_GIB,
2238 20 : panValues[j]);
2239 : }
2240 : }
2241 : }
2242 282 : else if (eOGRType == OFTReal)
2243 : {
2244 12 : PrintXMLDouble(m_fpXML.get(),
2245 : poFeature->GetFieldAsDouble(nFieldIdx));
2246 : }
2247 : else
2248 : {
2249 270 : VSIFPrintfL(
2250 : m_fpXML.get(), "%s",
2251 540 : XMLEscape(poFeature->GetFieldAsString(nFieldIdx))
2252 : .c_str());
2253 : }
2254 346 : break;
2255 : }
2256 : }
2257 : }
2258 :
2259 564 : if (!bWriteEltContent)
2260 82 : VSIFPrintfL(m_fpXML.get(), "\"");
2261 :
2262 564 : aoCurComponents = std::move(aoFieldComponents);
2263 564 : bCurIsRegularField = true;
2264 :
2265 564 : return true;
2266 : }
2267 :
2268 : /************************************************************************/
2269 : /* WriteFieldNoLink() */
2270 : /************************************************************************/
2271 :
2272 84 : bool GMLASWriter::WriteFieldNoLink(
2273 : OGRFeature *poFeature, const GMLASField &oField,
2274 : const LayerDescription &oLayerDesc, XPathComponents &aoLayerComponents,
2275 : XPathComponents &aoCurComponents, const XPathComponents &aoPrefixComponents,
2276 : const std::set<CPLString> &oSetLayersInIteration, int nRecLevel,
2277 : bool &bAtLeastOneFieldWritten, bool &bCurIsRegularField)
2278 : {
2279 84 : const auto oIter = m_oMapXPathToIdx.find(oField.GetRelatedClassXPath());
2280 84 : if (oIter == m_oMapXPathToIdx.end())
2281 : {
2282 : // Not necessary to be more verbose in case of truncated
2283 : // source dataset
2284 0 : CPLDebug("GMLAS", "No child layer of %s matching xpath = %s",
2285 : oLayerDesc.osName.c_str(),
2286 0 : oField.GetRelatedClassXPath().c_str());
2287 0 : return true;
2288 : }
2289 :
2290 84 : const LayerDescription &oChildLayerDesc = m_aoLayerDesc[oIter->second];
2291 84 : OGRLayer *poRelLayer = GetLayerByName(oChildLayerDesc.osName);
2292 84 : if (poRelLayer == nullptr)
2293 : {
2294 : // Not necessary to be more verbose in case of truncated
2295 : // source dataset
2296 0 : CPLDebug("GMLAS", "Child layer %s of %s not found",
2297 : oChildLayerDesc.osName.c_str(), oLayerDesc.osName.c_str());
2298 0 : return true;
2299 : }
2300 :
2301 84 : if (oLayerDesc.osPKIDName.empty())
2302 : {
2303 0 : CPLError(CE_Failure, CPLE_AppDefined, "Missing %s for layer %s",
2304 : szLAYER_PKID_NAME, oLayerDesc.osName.c_str());
2305 0 : return true;
2306 : }
2307 : int nParentPKIDIdx;
2308 84 : if ((nParentPKIDIdx =
2309 84 : oLayerDesc.GetOGRIdxFromFieldName(oLayerDesc.osPKIDName)) < 0)
2310 : {
2311 0 : CPLError(CE_Failure, CPLE_AppDefined,
2312 : "Cannot find field %s in layer %s",
2313 : oLayerDesc.osPKIDName.c_str(), oLayerDesc.osName.c_str());
2314 0 : return true;
2315 : }
2316 84 : if (!poFeature->IsFieldSetAndNotNull(nParentPKIDIdx))
2317 : {
2318 0 : CPLError(CE_Failure, CPLE_AppDefined,
2319 : "Missing value of %s field for feature " CPL_FRMT_GIB
2320 : " of layer %s",
2321 : oLayerDesc.osPKIDName.c_str(), poFeature->GetFID(),
2322 : oLayerDesc.osName.c_str());
2323 0 : return true;
2324 : }
2325 84 : if (oChildLayerDesc.osParentPKIDName.empty())
2326 : {
2327 0 : CPLError(CE_Failure, CPLE_AppDefined, "Missing %s for layer %s",
2328 : szLAYER_PARENT_PKID_NAME, oChildLayerDesc.osName.c_str());
2329 : }
2330 84 : if (oSetLayersInIteration.find(oChildLayerDesc.osName) !=
2331 168 : oSetLayersInIteration.end())
2332 : {
2333 0 : CPLDebug("GMLAS", "Unexpected at line %d", __LINE__);
2334 0 : return true;
2335 : }
2336 :
2337 168 : std::set<CPLString> oSetLayersInIterationSub(oSetLayersInIteration);
2338 84 : oSetLayersInIterationSub.insert(oChildLayerDesc.osName);
2339 :
2340 84 : if (aoLayerComponents.empty())
2341 : {
2342 64 : aoLayerComponents = SplitXPath(oLayerDesc.osXPath);
2343 64 : aoLayerComponents.insert(aoLayerComponents.begin(),
2344 : aoPrefixComponents.begin(),
2345 128 : aoPrefixComponents.end());
2346 : }
2347 :
2348 168 : XPathComponents aoFieldComponents = SplitXPath(oField.GetXPath());
2349 84 : aoFieldComponents.insert(aoFieldComponents.begin(),
2350 : aoPrefixComponents.begin(),
2351 168 : aoPrefixComponents.end());
2352 :
2353 168 : CPLString osParentPKID(poFeature->GetFieldAsString(nParentPKIDIdx));
2354 84 : poRelLayer->SetAttributeFilter(
2355 : CPLSPrintf("%s = '%s'", oChildLayerDesc.osParentPKIDName.c_str(),
2356 84 : osParentPKID.c_str()));
2357 84 : poRelLayer->ResetReading();
2358 :
2359 : auto poChildFeature =
2360 168 : std::unique_ptr<OGRFeature>(poRelLayer->GetNextFeature());
2361 168 : XPathComponents aoNewInitialContext;
2362 84 : if (poChildFeature != nullptr)
2363 : {
2364 120 : if (aoFieldComponents.size() == aoLayerComponents.size() + 1 &&
2365 44 : oField.GetRepetitionOnSequence())
2366 : {
2367 : /* Case of
2368 : <xs:element name="sequence_unbounded_dt_1">
2369 : <xs:complexType>
2370 : <xs:sequence maxOccurs="unbounded">
2371 : <xs:element name="subelement"
2372 : type="xs:dateTime"/>
2373 : </xs:sequence>
2374 : </xs:complexType>
2375 : </xs:element>
2376 : */
2377 8 : aoNewInitialContext = std::move(aoFieldComponents);
2378 : }
2379 68 : else if (aoFieldComponents.size() == aoLayerComponents.size() + 2)
2380 : {
2381 : /* Case of
2382 : <xs:element name="sequence_1_dt_unbounded">
2383 : <xs:complexType>
2384 : <xs:sequence>
2385 : <xs:element name="subelement"
2386 : type="xs:dateTime"
2387 : maxOccurs="unbounded"/>
2388 : </xs:sequence>
2389 : </xs:complexType>
2390 : </xs:element>
2391 : */
2392 16 : aoNewInitialContext = std::move(aoFieldComponents);
2393 16 : aoNewInitialContext.pop_back();
2394 : }
2395 : else
2396 : {
2397 : /* Case of
2398 : <xs:element name="unbounded_sequence_1_dt"
2399 : maxOccurs="unbounded">
2400 : <xs:complexType>
2401 : <xs:sequence>
2402 : <xs:element name="subelement"
2403 : type="xs:dateTime"/>
2404 : </xs:sequence>
2405 : </xs:complexType>
2406 : </xs:element>
2407 : */
2408 52 : aoNewInitialContext = std::move(aoLayerComponents);
2409 : }
2410 :
2411 76 : WriteClosingAndStartingTags(aoCurComponents, aoNewInitialContext,
2412 76 : bCurIsRegularField);
2413 :
2414 76 : bAtLeastOneFieldWritten = true;
2415 76 : aoCurComponents = aoNewInitialContext;
2416 76 : bCurIsRegularField = false;
2417 : }
2418 :
2419 244 : while (poChildFeature)
2420 : {
2421 160 : bool bRet = WriteFeature(poChildFeature.get(), oChildLayerDesc,
2422 : oSetLayersInIterationSub, aoNewInitialContext,
2423 : aoPrefixComponents, nRecLevel + 1);
2424 :
2425 160 : if (!bRet)
2426 0 : return false;
2427 :
2428 160 : poChildFeature.reset(poRelLayer->GetNextFeature());
2429 : }
2430 84 : poRelLayer->ResetReading();
2431 :
2432 84 : return true;
2433 : }
2434 :
2435 : /************************************************************************/
2436 : /* GetFilteredLayer() */
2437 : /************************************************************************/
2438 :
2439 : OGRLayer *
2440 52 : GMLASWriter::GetFilteredLayer(OGRLayer *poSrcLayer, const CPLString &osFilter,
2441 : const std::set<CPLString> &oSetLayersInIteration)
2442 : {
2443 52 : if (oSetLayersInIteration.find(poSrcLayer->GetName()) ==
2444 104 : oSetLayersInIteration.end())
2445 : {
2446 32 : poSrcLayer->SetAttributeFilter(osFilter);
2447 32 : poSrcLayer->ResetReading();
2448 32 : return poSrcLayer;
2449 : }
2450 :
2451 : // RDBMS drivers will really create a new iterator independent of the
2452 : // underlying layer when using a SELECT statement
2453 20 : GDALDriver *poDriver = m_poSrcDS->GetDriver();
2454 40 : if (poDriver != nullptr &&
2455 20 : (EQUAL(poDriver->GetDescription(), "SQLite") ||
2456 0 : EQUAL(poDriver->GetDescription(), "PostgreSQL")))
2457 : {
2458 40 : CPLString osSQL;
2459 20 : osSQL.Printf("SELECT * FROM \"%s\" WHERE %s", poSrcLayer->GetName(),
2460 20 : osFilter.c_str());
2461 20 : return m_poSrcDS->ExecuteSQL(osSQL, nullptr, nullptr);
2462 : }
2463 :
2464 : // TODO ?
2465 0 : CPLDebug("GMLAS", "Cannot recursively iterate on %s on this driver",
2466 0 : poSrcLayer->GetName());
2467 0 : return nullptr;
2468 : }
2469 :
2470 : /************************************************************************/
2471 : /* ReleaseFilteredLayer() */
2472 : /************************************************************************/
2473 :
2474 52 : void GMLASWriter::ReleaseFilteredLayer(OGRLayer *poSrcLayer,
2475 : OGRLayer *poIterLayer)
2476 : {
2477 52 : if (poIterLayer != poSrcLayer)
2478 20 : m_poSrcDS->ReleaseResultSet(poIterLayer);
2479 : else
2480 32 : poSrcLayer->ResetReading();
2481 52 : }
2482 :
2483 : /************************************************************************/
2484 : /* WriteFieldWithLink() */
2485 : /************************************************************************/
2486 :
2487 36 : bool GMLASWriter::WriteFieldWithLink(
2488 : OGRFeature *poFeature, const GMLASField &oField,
2489 : const LayerDescription &oLayerDesc, XPathComponents &aoLayerComponents,
2490 : XPathComponents &aoCurComponents, const XPathComponents &aoPrefixComponents,
2491 : const std::set<CPLString> &oSetLayersInIteration, int nRecLevel,
2492 : bool &bAtLeastOneFieldWritten, bool &bCurIsRegularField)
2493 : {
2494 36 : const auto oIter = m_oMapXPathToIdx.find(oField.GetRelatedClassXPath());
2495 36 : if (oIter == m_oMapXPathToIdx.end())
2496 : {
2497 : // Not necessary to be more verbose in case of truncated
2498 : // source dataset
2499 0 : CPLDebug("GMLAS", "No child layer of %s matching xpath = %s",
2500 : oLayerDesc.osName.c_str(),
2501 0 : oField.GetRelatedClassXPath().c_str());
2502 0 : return true;
2503 : }
2504 :
2505 36 : const LayerDescription &oChildLayerDesc = m_aoLayerDesc[oIter->second];
2506 36 : OGRLayer *poRelLayer = GetLayerByName(oChildLayerDesc.osName);
2507 36 : if (poRelLayer == nullptr)
2508 : {
2509 : // Not necessary to be more verbose in case of truncated
2510 : // source dataset
2511 0 : CPLDebug("GMLAS", "Referenced layer %s of %s not found",
2512 : oChildLayerDesc.osName.c_str(), oLayerDesc.osName.c_str());
2513 0 : return true;
2514 : }
2515 :
2516 36 : const int nFieldIdx = oLayerDesc.GetOGRIdxFromFieldName(oField.GetName());
2517 72 : XPathComponents aoFieldComponents = SplitXPath(oField.GetXPath());
2518 36 : aoFieldComponents.insert(aoFieldComponents.begin(),
2519 : aoPrefixComponents.begin(),
2520 72 : aoPrefixComponents.end());
2521 :
2522 36 : if (nFieldIdx < 0)
2523 : {
2524 0 : CPLError(CE_Failure, CPLE_AppDefined, "Missing field %s for layer %s",
2525 0 : oField.GetName().c_str(), oLayerDesc.osName.c_str());
2526 0 : return true;
2527 : }
2528 36 : if (!poFeature->IsFieldSetAndNotNull(nFieldIdx))
2529 : {
2530 : // Not an error (unless the field is required)
2531 4 : return true;
2532 : }
2533 32 : if (oLayerDesc.osPKIDName.empty())
2534 : {
2535 0 : CPLError(CE_Failure, CPLE_AppDefined, "Missing %s for layer %s",
2536 : szLAYER_PKID_NAME, oLayerDesc.osName.c_str());
2537 0 : return true;
2538 : }
2539 32 : if (oChildLayerDesc.osPKIDName.empty())
2540 : {
2541 0 : CPLError(CE_Failure, CPLE_AppDefined, "Missing %s for layer %s",
2542 : szLAYER_PKID_NAME, oChildLayerDesc.osName.c_str());
2543 0 : return true;
2544 : }
2545 32 : if (aoFieldComponents.size() < 2)
2546 : {
2547 : // Shouldn't happen for well behaved metadata
2548 0 : CPLDebug("GMLAS", "Unexpected at line %d", __LINE__);
2549 0 : return true;
2550 : }
2551 64 : if (oChildLayerDesc.osXPath.empty() ||
2552 32 : aoFieldComponents.back() != SplitXPath(oChildLayerDesc.osXPath).front())
2553 : {
2554 : // Shouldn't happen for well behaved metadata
2555 0 : CPLDebug("GMLAS", "Unexpected at line %d", __LINE__);
2556 0 : return true;
2557 : }
2558 :
2559 64 : CPLString osChildPKID(poFeature->GetFieldAsString(nFieldIdx));
2560 : const CPLString osFilter(CPLSPrintf(
2561 64 : "%s = '%s'", oChildLayerDesc.osPKIDName.c_str(), osChildPKID.c_str()));
2562 : OGRLayer *poIterLayer =
2563 32 : GetFilteredLayer(poRelLayer, osFilter, oSetLayersInIteration);
2564 32 : if (poIterLayer == nullptr)
2565 : {
2566 0 : return true;
2567 : }
2568 :
2569 64 : std::set<CPLString> oSetLayersInIterationSub(oSetLayersInIteration);
2570 32 : oSetLayersInIterationSub.insert(oChildLayerDesc.osName);
2571 :
2572 64 : XPathComponents aoPrefixComponentsNew(aoFieldComponents);
2573 32 : aoPrefixComponentsNew.pop_back();
2574 :
2575 32 : if (aoLayerComponents.empty())
2576 : {
2577 32 : aoLayerComponents = SplitXPath(oLayerDesc.osXPath);
2578 32 : aoLayerComponents.insert(aoLayerComponents.begin(),
2579 : aoPrefixComponents.begin(),
2580 64 : aoPrefixComponents.end());
2581 : }
2582 :
2583 : auto poChildFeature =
2584 64 : std::unique_ptr<OGRFeature>(poIterLayer->GetNextFeature());
2585 32 : XPathComponents aoInitialComponents;
2586 32 : const bool bHasChild = poChildFeature != nullptr;
2587 32 : if (bHasChild)
2588 : {
2589 32 : aoInitialComponents = std::move(aoFieldComponents);
2590 32 : if (!aoInitialComponents.empty())
2591 32 : aoInitialComponents.pop_back();
2592 32 : WriteClosingAndStartingTags(aoCurComponents, aoInitialComponents,
2593 32 : bCurIsRegularField);
2594 : }
2595 :
2596 32 : bool bRet = true;
2597 64 : while (poChildFeature)
2598 : {
2599 32 : bRet = WriteFeature(poChildFeature.get(), oChildLayerDesc,
2600 : oSetLayersInIterationSub, aoInitialComponents,
2601 : aoPrefixComponentsNew, nRecLevel + 1);
2602 32 : if (!bRet)
2603 0 : break;
2604 32 : poChildFeature.reset(poIterLayer->GetNextFeature());
2605 : }
2606 32 : ReleaseFilteredLayer(poRelLayer, poIterLayer);
2607 :
2608 32 : if (bHasChild)
2609 : {
2610 32 : bAtLeastOneFieldWritten = true;
2611 32 : aoCurComponents = std::move(aoInitialComponents);
2612 32 : bCurIsRegularField = false;
2613 : }
2614 :
2615 32 : return bRet;
2616 : }
2617 :
2618 : /************************************************************************/
2619 : /* WriteFieldJunctionTable() */
2620 : /************************************************************************/
2621 :
2622 16 : bool GMLASWriter::WriteFieldJunctionTable(
2623 : OGRFeature *poFeature, const GMLASField &oField,
2624 : const LayerDescription &oLayerDesc,
2625 : XPathComponents & /*aoLayerComponents */, XPathComponents &aoCurComponents,
2626 : const XPathComponents &aoPrefixComponents,
2627 : const std::set<CPLString> &oSetLayersInIteration, int nRecLevel,
2628 : bool &bAtLeastOneFieldWritten, bool &bCurIsRegularField)
2629 : {
2630 16 : const auto oIter = m_oMapXPathToIdx.find(oField.GetRelatedClassXPath());
2631 16 : if (oIter == m_oMapXPathToIdx.end())
2632 : {
2633 : // Not necessary to be more verbose in case of truncated
2634 : // source dataset
2635 0 : CPLDebug("GMLAS", "No related layer of %s matching xpath = %s",
2636 : oLayerDesc.osName.c_str(),
2637 0 : oField.GetRelatedClassXPath().c_str());
2638 0 : return true;
2639 : }
2640 :
2641 16 : const LayerDescription &oRelLayerDesc = m_aoLayerDesc[oIter->second];
2642 16 : OGRLayer *poRelLayer = GetLayerByName(oRelLayerDesc.osName);
2643 16 : OGRLayer *poJunctionLayer = GetLayerByName(oField.GetJunctionLayer());
2644 16 : if (poRelLayer == nullptr)
2645 : {
2646 : // Not necessary to be more verbose in case of truncated
2647 : // source dataset
2648 0 : CPLDebug("GMLAS", "Referenced layer %s of %s not found",
2649 : oRelLayerDesc.osName.c_str(), oLayerDesc.osName.c_str());
2650 0 : return true;
2651 : }
2652 16 : if (poJunctionLayer == nullptr)
2653 : {
2654 : // Not necessary to be more verbose in case of truncated
2655 : // source dataset
2656 0 : CPLDebug("GMLAS", "Junction layer %s not found",
2657 0 : oField.GetJunctionLayer().c_str());
2658 0 : return true;
2659 : }
2660 :
2661 16 : int nIndexPKID = -1;
2662 16 : if (oLayerDesc.osPKIDName.empty())
2663 : {
2664 0 : CPLError(CE_Failure, CPLE_AppDefined, "Missing %s for layer %s",
2665 : szLAYER_PKID_NAME, oLayerDesc.osName.c_str());
2666 0 : return true;
2667 : }
2668 16 : if ((nIndexPKID =
2669 16 : oLayerDesc.GetOGRIdxFromFieldName(oLayerDesc.osPKIDName)) < 0)
2670 : {
2671 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s='%s' in layer %s",
2672 : szLAYER_PKID_NAME, oLayerDesc.osPKIDName.c_str(),
2673 : oLayerDesc.osName.c_str());
2674 0 : return true;
2675 : }
2676 16 : if (!poFeature->IsFieldSetAndNotNull(nIndexPKID))
2677 : {
2678 0 : CPLError(CE_Failure, CPLE_AppDefined,
2679 : "Field '%s' in layer %s is not set for "
2680 : "feature " CPL_FRMT_GIB,
2681 : oLayerDesc.osPKIDName.c_str(), oLayerDesc.osName.c_str(),
2682 : poFeature->GetFID());
2683 0 : return true;
2684 : }
2685 16 : if (oRelLayerDesc.osPKIDName.empty())
2686 : {
2687 0 : CPLError(CE_Failure, CPLE_AppDefined, "Missing %s for layer %s",
2688 : szLAYER_PKID_NAME, oRelLayerDesc.osName.c_str());
2689 0 : return true;
2690 : }
2691 16 : if (oSetLayersInIteration.find(oRelLayerDesc.osName) !=
2692 32 : oSetLayersInIteration.end())
2693 : {
2694 : // TODO... cycle situation. We will need to open a new
2695 : // source dataset or something
2696 0 : return true;
2697 : }
2698 :
2699 32 : std::set<CPLString> oSetLayersInIterationSub(oSetLayersInIteration);
2700 16 : oSetLayersInIterationSub.insert(oRelLayerDesc.osName);
2701 :
2702 16 : poJunctionLayer->SetAttributeFilter(CPLSPrintf(
2703 16 : "%s = '%s'", szPARENT_PKID, poFeature->GetFieldAsString(nIndexPKID)));
2704 16 : poJunctionLayer->ResetReading();
2705 32 : std::vector<CPLString> aoChildPKIDs;
2706 36 : for (auto &&poJunctionFeature : *poJunctionLayer)
2707 : {
2708 20 : aoChildPKIDs.push_back(
2709 : poJunctionFeature->GetFieldAsString(szCHILD_PKID));
2710 : }
2711 16 : poJunctionLayer->ResetReading();
2712 :
2713 16 : bool bRet = true;
2714 16 : bool bHasChild = false;
2715 32 : XPathComponents aoInitialComponents;
2716 36 : for (size_t j = 0; bRet && j < aoChildPKIDs.size(); j++)
2717 : {
2718 20 : CPLString osFilter;
2719 : osFilter.Printf("%s = '%s'", oRelLayerDesc.osPKIDName.c_str(),
2720 20 : aoChildPKIDs[j].c_str());
2721 : OGRLayer *poIterLayer =
2722 20 : GetFilteredLayer(poRelLayer, osFilter, oSetLayersInIteration);
2723 20 : if (poIterLayer == nullptr)
2724 : {
2725 0 : return true;
2726 : }
2727 :
2728 : auto poChildFeature =
2729 40 : std::unique_ptr<OGRFeature>(poIterLayer->GetNextFeature());
2730 20 : if (poChildFeature != nullptr)
2731 : {
2732 20 : if (!bHasChild)
2733 : {
2734 12 : bHasChild = true;
2735 :
2736 12 : aoInitialComponents = SplitXPath(oField.GetXPath());
2737 12 : aoInitialComponents.insert(aoInitialComponents.begin(),
2738 : aoPrefixComponents.begin(),
2739 24 : aoPrefixComponents.end());
2740 :
2741 12 : if (!aoInitialComponents.empty())
2742 12 : aoInitialComponents.pop_back();
2743 12 : WriteClosingAndStartingTags(
2744 12 : aoCurComponents, aoInitialComponents, bCurIsRegularField);
2745 : }
2746 :
2747 20 : bRet = WriteFeature(poChildFeature.get(), oRelLayerDesc,
2748 40 : oSetLayersInIterationSub, XPathComponents(),
2749 40 : XPathComponents(), nRecLevel + 1);
2750 :
2751 20 : ReleaseFilteredLayer(poRelLayer, poIterLayer);
2752 : }
2753 : else
2754 : {
2755 0 : ReleaseFilteredLayer(poRelLayer, poIterLayer);
2756 : }
2757 : }
2758 :
2759 16 : if (bHasChild)
2760 : {
2761 12 : bAtLeastOneFieldWritten = true;
2762 12 : aoCurComponents = std::move(aoInitialComponents);
2763 12 : bCurIsRegularField = false;
2764 : }
2765 :
2766 16 : return bRet;
2767 : }
2768 :
2769 : /************************************************************************/
2770 : /* PrintIndent() */
2771 : /************************************************************************/
2772 :
2773 870 : void GMLASWriter::PrintIndent(VSILFILE *fp)
2774 : {
2775 3888 : for (int i = 0; i < m_nIndentLevel; i++)
2776 : {
2777 3018 : VSIFWriteL(m_osIndentation.c_str(), 1, m_osIndentation.size(), fp);
2778 : }
2779 870 : }
2780 :
2781 : /************************************************************************/
2782 : /* PrintLine() */
2783 : /************************************************************************/
2784 :
2785 1100 : void GMLASWriter::PrintLine(VSILFILE *fp, const char *fmt, ...)
2786 : {
2787 2200 : CPLString osWork;
2788 : va_list args;
2789 :
2790 1100 : va_start(args, fmt);
2791 1100 : osWork.vPrintf(fmt, args);
2792 1100 : va_end(args);
2793 :
2794 1100 : VSIFWriteL(osWork.c_str(), 1, osWork.size(), fp);
2795 1100 : VSIFWriteL(m_osEOL.c_str(), 1, m_osEOL.size(), fp);
2796 1100 : }
2797 :
2798 : } /* namespace GMLAS */
2799 :
2800 : /************************************************************************/
2801 : /* GMLASFakeDataset */
2802 : /************************************************************************/
2803 :
2804 : class GMLASFakeDataset final : public GDALDataset
2805 : {
2806 : public:
2807 0 : GMLASFakeDataset()
2808 0 : {
2809 0 : }
2810 : };
2811 :
2812 : /************************************************************************/
2813 : /* OGRGMLASDriverCreateCopy() */
2814 : /************************************************************************/
2815 :
2816 22 : GDALDataset *OGRGMLASDriverCreateCopy(const char *pszFilename,
2817 : GDALDataset *poSrcDS, int /*bStrict*/,
2818 : char **papszOptions,
2819 : GDALProgressFunc pfnProgress,
2820 : void *pProgressData)
2821 : {
2822 22 : if (strcmp(CPLGetExtension(pszFilename), "xsd") == 0)
2823 : {
2824 1 : CPLError(CE_Failure, CPLE_AppDefined, ".xsd extension is not valid");
2825 1 : return nullptr;
2826 : }
2827 :
2828 : // Strip GMLAS: prefix if specified
2829 21 : if (STARTS_WITH_CI(pszFilename, szGMLAS_PREFIX))
2830 1 : pszFilename += strlen(szGMLAS_PREFIX);
2831 :
2832 42 : GMLAS::GMLASWriter oWriter(pszFilename, poSrcDS, papszOptions);
2833 21 : if (!oWriter.Write(pfnProgress, pProgressData))
2834 11 : return nullptr;
2835 :
2836 20 : if (CPLString(pszFilename) == "/vsistdout/" ||
2837 : // This option is mostly useful for tests where we don't want
2838 : // WFS 2.0 schemas to be pulled from the network
2839 10 : !CPLFetchBool(papszOptions, "REOPEN_DATASET_WITH_GMLAS", true))
2840 : {
2841 0 : return new GMLASFakeDataset();
2842 : }
2843 : else
2844 : {
2845 : GDALOpenInfo oOpenInfo(
2846 30 : (CPLString(szGMLAS_PREFIX) + pszFilename).c_str(), GA_ReadOnly);
2847 20 : auto poOutDS = std::make_unique<OGRGMLASDataSource>();
2848 10 : if (!poOutDS->Open(&oOpenInfo))
2849 : {
2850 0 : poOutDS.reset();
2851 : }
2852 10 : return poOutDS.release();
2853 : }
2854 : }
|