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