1 /**********************************************************************
2  *
3  * Project:  GML Reader
4  * Purpose:  Implementation of GMLFeatureClass.
5  * Author:   Frank Warmerdam, warmerdam@pobox.com
6  *
7  **********************************************************************
8  * Copyright (c) 2002, Frank Warmerdam
9  * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include "cpl_port.h"
31 #include "gmlreader.h"
32 
33 #include <cmath>
34 #include <cstddef>
35 #include <cstdio>
36 #include <cstdlib>
37 #include <cstring>
38 #include <string>
39 
40 #include "cpl_conv.h"
41 #include "cpl_error.h"
42 #include "cpl_minixml.h"
43 #include "cpl_string.h"
44 #include "ogr_core.h"
45 #include "ogr_p.h"
46 #include "ogr_geometry.h"
47 
48 CPL_CVSID("$Id: gmlfeatureclass.cpp 4a9078b00804fc0de0063d36cbbde997a24c087a 2021-09-18 15:37:17 +0200 Even Rouault $")
49 
50 /************************************************************************/
51 /*                          GMLFeatureClass()                           */
52 /************************************************************************/
53 
GMLFeatureClass(const char * pszName)54 GMLFeatureClass::GMLFeatureClass( const char *pszName ) :
55     m_pszName(CPLStrdup(pszName)),
56     m_pszElementName(nullptr),
57     n_nNameLen(static_cast<int>(strlen(pszName))),
58     n_nElementNameLen(0),
59     m_nPropertyCount(0),
60     m_papoProperty(nullptr),
61     m_nGeometryPropertyCount(0),
62     m_papoGeometryProperty(nullptr),
63     m_bSchemaLocked(false),
64     m_nFeatureCount(-1),  // Unknown.
65     m_pszExtraInfo(nullptr),
66     m_bHaveExtents(false),
67     m_dfXMin(0.0),
68     m_dfXMax(0.0),
69     m_dfYMin(0.0),
70     m_dfYMax(0.0),
71     m_pszSRSName(nullptr),
72     m_bSRSNameConsistent(true)
73 {}
74 
75 /************************************************************************/
76 /*                          ~GMLFeatureClass()                          */
77 /************************************************************************/
78 
~GMLFeatureClass()79 GMLFeatureClass::~GMLFeatureClass()
80 
81 {
82     CPLFree(m_pszName);
83     CPLFree(m_pszElementName);
84 
85     for( int i = 0; i < m_nPropertyCount; i++ )
86         delete m_papoProperty[i];
87     CPLFree(m_papoProperty);
88 
89     ClearGeometryProperties();
90 
91     CPLFree(m_pszSRSName);
92 }
93 
94 /************************************************************************/
95 /*                         StealProperties()                            */
96 /************************************************************************/
97 
StealProperties()98 void GMLFeatureClass::StealProperties()
99 {
100     m_nPropertyCount = 0;
101     CPLFree(m_papoProperty);
102     m_papoProperty = nullptr;
103     m_oMapPropertyNameToIndex.clear();
104     m_oMapPropertySrcElementToIndex.clear();
105 }
106 
107 /************************************************************************/
108 /*                       StealGeometryProperties()                      */
109 /************************************************************************/
110 
StealGeometryProperties()111 void GMLFeatureClass::StealGeometryProperties()
112 {
113     m_nGeometryPropertyCount = 0;
114     CPLFree(m_papoGeometryProperty);
115     m_papoGeometryProperty = nullptr;
116 }
117 
118 /************************************************************************/
119 /*                            SetName()                                 */
120 /************************************************************************/
121 
SetName(const char * pszNewName)122 void GMLFeatureClass::SetName(const char *pszNewName)
123 {
124     CPLFree(m_pszName);
125     m_pszName = CPLStrdup(pszNewName);
126 }
127 
128 /************************************************************************/
129 /*                           GetProperty(int)                           */
130 /************************************************************************/
131 
GetProperty(int iIndex) const132 GMLPropertyDefn *GMLFeatureClass::GetProperty( int iIndex ) const
133 
134 {
135     if( iIndex < 0 || iIndex >= m_nPropertyCount )
136         return nullptr;
137 
138     return m_papoProperty[iIndex];
139 }
140 
141 /************************************************************************/
142 /*                          GetPropertyIndex()                          */
143 /************************************************************************/
144 
GetPropertyIndex(const char * pszName) const145 int GMLFeatureClass::GetPropertyIndex( const char *pszName ) const
146 
147 {
148     auto oIter = m_oMapPropertyNameToIndex.find(CPLString(pszName).toupper());
149     if( oIter != m_oMapPropertyNameToIndex.end() )
150         return oIter->second;
151 
152     return -1;
153 }
154 
155 /************************************************************************/
156 /*                        GetPropertyIndexBySrcElement()                */
157 /************************************************************************/
158 
GetPropertyIndexBySrcElement(const char * pszElement,int nLen) const159 int GMLFeatureClass::GetPropertyIndexBySrcElement( const char *pszElement,
160                                                    int nLen ) const
161 
162 {
163     auto oIter = m_oMapPropertySrcElementToIndex.find(CPLString(pszElement, nLen));
164     if( oIter != m_oMapPropertySrcElementToIndex.end() )
165         return oIter->second;
166 
167     return -1;
168 }
169 
170 /************************************************************************/
171 /*                            AddProperty()                             */
172 /************************************************************************/
173 
AddProperty(GMLPropertyDefn * poDefn)174 int GMLFeatureClass::AddProperty( GMLPropertyDefn *poDefn )
175 
176 {
177     if( GetProperty(poDefn->GetName()) != nullptr )
178     {
179         CPLError(CE_Warning, CPLE_AppDefined,
180                  "Field with same name (%s) already exists in (%s). "
181                  "Skipping newer ones",
182                  poDefn->GetName(), m_pszName);
183         return -1;
184     }
185 
186     m_nPropertyCount++;
187     m_papoProperty = static_cast<GMLPropertyDefn **>(
188         CPLRealloc(m_papoProperty, sizeof(void *) * m_nPropertyCount));
189 
190     m_papoProperty[m_nPropertyCount - 1] = poDefn;
191     m_oMapPropertyNameToIndex[ CPLString(poDefn->GetName()).toupper() ] =
192         m_nPropertyCount - 1;
193     if( m_oMapPropertySrcElementToIndex.find(poDefn->GetSrcElement()) ==
194             m_oMapPropertySrcElementToIndex.end() )
195     {
196         m_oMapPropertySrcElementToIndex[ poDefn->GetSrcElement() ] =
197             m_nPropertyCount - 1;
198     }
199 
200     return m_nPropertyCount - 1;
201 }
202 
203 /************************************************************************/
204 /*                         GetGeometryProperty(int)                      */
205 /************************************************************************/
206 
207 GMLGeometryPropertyDefn *
GetGeometryProperty(int iIndex) const208 GMLFeatureClass::GetGeometryProperty( int iIndex ) const
209 {
210     if( iIndex < 0 || iIndex >= m_nGeometryPropertyCount )
211         return nullptr;
212 
213     return m_papoGeometryProperty[iIndex];
214 }
215 
216 /************************************************************************/
217 /*                   GetGeometryPropertyIndexBySrcElement()             */
218 /************************************************************************/
219 
GetGeometryPropertyIndexBySrcElement(const char * pszElement) const220 int GMLFeatureClass::GetGeometryPropertyIndexBySrcElement(
221     const char *pszElement) const
222 
223 {
224     for( int i = 0; i < m_nGeometryPropertyCount; i++ )
225         if( strcmp(pszElement, m_papoGeometryProperty[i]->GetSrcElement()) == 0 )
226             return i;
227 
228     return -1;
229 }
230 
231 /************************************************************************/
232 /*                         AddGeometryProperty()                        */
233 /************************************************************************/
234 
AddGeometryProperty(GMLGeometryPropertyDefn * poDefn)235 int GMLFeatureClass::AddGeometryProperty( GMLGeometryPropertyDefn *poDefn )
236 
237 {
238     if( GetGeometryPropertyIndexBySrcElement(poDefn->GetSrcElement()) >= 0 )
239     {
240         CPLError(CE_Warning, CPLE_AppDefined,
241                  "Geometry field with same name (%s) already exists in (%s). "
242                  "Skipping newer ones",
243                  poDefn->GetSrcElement(), m_pszName);
244         return -1;
245     }
246 
247     m_nGeometryPropertyCount++;
248     m_papoGeometryProperty = static_cast<GMLGeometryPropertyDefn **>(CPLRealloc(
249         m_papoGeometryProperty, sizeof(void *) * m_nGeometryPropertyCount));
250 
251     m_papoGeometryProperty[m_nGeometryPropertyCount - 1] = poDefn;
252 
253     return m_nGeometryPropertyCount - 1;
254 }
255 
256 /************************************************************************/
257 /*                       ClearGeometryProperties()                      */
258 /************************************************************************/
259 
ClearGeometryProperties()260 void GMLFeatureClass::ClearGeometryProperties()
261 {
262     for( int i = 0; i < m_nGeometryPropertyCount; i++ )
263         delete m_papoGeometryProperty[i];
264     CPLFree(m_papoGeometryProperty);
265     m_nGeometryPropertyCount = 0;
266     m_papoGeometryProperty = nullptr;
267 }
268 
269 /************************************************************************/
270 /*                         HasFeatureProperties()                       */
271 /************************************************************************/
272 
HasFeatureProperties()273 bool GMLFeatureClass::HasFeatureProperties()
274 {
275     for( int i = 0; i < m_nPropertyCount; i++ )
276     {
277         if( m_papoProperty[i]->GetType() == GMLPT_FeatureProperty ||
278             m_papoProperty[i]->GetType() == GMLPT_FeaturePropertyList )
279             return true;
280     }
281     return false;
282 }
283 
284 /************************************************************************/
285 /*                           SetElementName()                           */
286 /************************************************************************/
287 
SetElementName(const char * pszElementName)288 void GMLFeatureClass::SetElementName( const char *pszElementName )
289 
290 {
291     CPLFree(m_pszElementName);
292     m_pszElementName = CPLStrdup(pszElementName);
293     n_nElementNameLen = static_cast<int>(strlen(pszElementName));
294 }
295 
296 /************************************************************************/
297 /*                           GetElementName()                           */
298 /************************************************************************/
299 
GetElementName() const300 const char *GMLFeatureClass::GetElementName() const
301 
302 {
303     if( m_pszElementName == nullptr )
304         return m_pszName;
305 
306     return m_pszElementName;
307 }
308 
309 /************************************************************************/
310 /*                           GetElementName()                           */
311 /************************************************************************/
312 
GetElementNameLen() const313 size_t GMLFeatureClass::GetElementNameLen() const
314 
315 {
316     if( m_pszElementName == nullptr )
317         return n_nNameLen;
318 
319     return n_nElementNameLen;
320 }
321 
322 /************************************************************************/
323 /*                         GetFeatureCount()                          */
324 /************************************************************************/
325 
GetFeatureCount()326 GIntBig GMLFeatureClass::GetFeatureCount() { return m_nFeatureCount; }
327 
328 /************************************************************************/
329 /*                          SetFeatureCount()                           */
330 /************************************************************************/
331 
SetFeatureCount(GIntBig nNewCount)332 void GMLFeatureClass::SetFeatureCount( GIntBig nNewCount )
333 
334 {
335     m_nFeatureCount = nNewCount;
336 }
337 
338 /************************************************************************/
339 /*                            GetExtraInfo()                            */
340 /************************************************************************/
341 
GetExtraInfo()342 const char *GMLFeatureClass::GetExtraInfo() { return m_pszExtraInfo; }
343 
344 /************************************************************************/
345 /*                            SetExtraInfo()                            */
346 /************************************************************************/
347 
SetExtraInfo(const char * pszExtraInfo)348 void GMLFeatureClass::SetExtraInfo( const char *pszExtraInfo )
349 
350 {
351     CPLFree(m_pszExtraInfo);
352     m_pszExtraInfo = nullptr;
353 
354     if( pszExtraInfo != nullptr )
355         m_pszExtraInfo = CPLStrdup(pszExtraInfo);
356 }
357 
358 /************************************************************************/
359 /*                             SetExtents()                             */
360 /************************************************************************/
361 
SetExtents(double dfXMin,double dfXMax,double dfYMin,double dfYMax)362 void GMLFeatureClass::SetExtents( double dfXMin, double dfXMax,
363                                   double dfYMin, double dfYMax )
364 
365 {
366     m_dfXMin = dfXMin;
367     m_dfXMax = dfXMax;
368     m_dfYMin = dfYMin;
369     m_dfYMax = dfYMax;
370 
371     m_bHaveExtents = true;
372 }
373 
374 /************************************************************************/
375 /*                             GetExtents()                             */
376 /************************************************************************/
377 
GetExtents(double * pdfXMin,double * pdfXMax,double * pdfYMin,double * pdfYMax)378 bool GMLFeatureClass::GetExtents( double *pdfXMin, double *pdfXMax,
379                                   double *pdfYMin, double *pdfYMax )
380 
381 {
382     if( m_bHaveExtents )
383     {
384         *pdfXMin = m_dfXMin;
385         *pdfXMax = m_dfXMax;
386         *pdfYMin = m_dfYMin;
387         *pdfYMax = m_dfYMax;
388     }
389 
390     return m_bHaveExtents;
391 }
392 
393 /************************************************************************/
394 /*                            SetSRSName()                              */
395 /************************************************************************/
396 
SetSRSName(const char * pszSRSName)397 void GMLFeatureClass::SetSRSName( const char* pszSRSName )
398 
399 {
400     m_bSRSNameConsistent = true;
401     CPLFree(m_pszSRSName);
402     m_pszSRSName = pszSRSName ? CPLStrdup(pszSRSName) : nullptr;
403 }
404 
405 /************************************************************************/
406 /*                           MergeSRSName()                             */
407 /************************************************************************/
408 
MergeSRSName(const char * pszSRSName)409 void GMLFeatureClass::MergeSRSName( const char *pszSRSName )
410 
411 {
412     if(!m_bSRSNameConsistent)
413         return;
414 
415     if( m_pszSRSName == nullptr )
416     {
417         if (pszSRSName)
418             m_pszSRSName = CPLStrdup(pszSRSName);
419     }
420     else
421     {
422         m_bSRSNameConsistent =
423             pszSRSName != nullptr && strcmp(m_pszSRSName, pszSRSName) == 0;
424         if (!m_bSRSNameConsistent)
425         {
426             CPLFree(m_pszSRSName);
427             m_pszSRSName = nullptr;
428         }
429     }
430 }
431 
432 /************************************************************************/
433 /*                         InitializeFromXML()                          */
434 /************************************************************************/
435 
InitializeFromXML(CPLXMLNode * psRoot)436 bool GMLFeatureClass::InitializeFromXML( CPLXMLNode *psRoot )
437 
438 {
439     // Do some rudimentary checking that this is a well formed node.
440     if( psRoot == nullptr || psRoot->eType != CXT_Element ||
441         !EQUAL(psRoot->pszValue, "GMLFeatureClass") )
442     {
443         CPLError(CE_Failure, CPLE_AppDefined,
444                  "GMLFeatureClass::InitializeFromXML() called on %s node!",
445                  psRoot ? psRoot->pszValue : "(null)");
446         return false;
447     }
448 
449     if( CPLGetXMLValue( psRoot, "Name", nullptr ) == nullptr )
450     {
451         CPLError(CE_Failure, CPLE_AppDefined,
452                  "GMLFeatureClass has no <Name> element.");
453         return false;
454     }
455 
456     // Collect base info.
457     CPLFree(m_pszName);
458     m_pszName = CPLStrdup(CPLGetXMLValue(psRoot, "Name", nullptr));
459     n_nNameLen = static_cast<int>(strlen(m_pszName));
460 
461     SetElementName(CPLGetXMLValue(psRoot, "ElementPath", m_pszName));
462 
463     // Collect geometry properties.
464     bool bHasValidGeometryName = false;
465     bool bHasValidGeometryElementPath = false;
466     bool bHasFoundGeomType = false;
467     bool bHasFoundGeomElements = false;
468     const char *pszGName = "";
469     const char *pszGPath = "";
470     int nGeomType = wkbUnknown;
471 
472     const auto FlattenGeomTypeFromInt = [] (int eType)
473     {
474         eType = eType & (~wkb25DBitInternalUse);
475         if( eType >= 1000 && eType < 2000 )  // ISO Z.
476             return eType - 1000;
477         if( eType >= 2000 && eType < 3000 )  // ISO M.
478             return eType - 2000;
479         if( eType >= 3000 && eType < 4000 )  // ISO ZM.
480             return eType - 3000;
481         return eType;
482     };
483 
484     CPLXMLNode *psThis = nullptr;
485     for( psThis = psRoot->psChild; psThis != nullptr; psThis = psThis->psNext )
486     {
487         if( psThis->eType == CXT_Element &&
488             EQUAL(psThis->pszValue, "GeomPropertyDefn") )
489         {
490             const char *pszName = CPLGetXMLValue(psThis, "Name", "");
491             const char *pszElementPath =
492                 CPLGetXMLValue(psThis, "ElementPath", "");
493             const char *pszType = CPLGetXMLValue(psThis, "Type", nullptr);
494             const bool bNullable =
495                 CPLTestBool(CPLGetXMLValue(psThis, "Nullable", "true"));
496             nGeomType = wkbUnknown;
497             if( pszType != nullptr && !EQUAL(pszType, "0") )
498             {
499                 nGeomType = atoi(pszType);
500                 const int nFlattenGeomType = FlattenGeomTypeFromInt(nGeomType);
501                 if( nGeomType != 0 &&
502                     !(nFlattenGeomType >= static_cast<int>(wkbPoint) &&
503                       nFlattenGeomType <= static_cast<int>(wkbTIN)) )
504                 {
505                     nGeomType = wkbUnknown;
506                     CPLError(CE_Warning, CPLE_AppDefined,
507                              "Unrecognized geometry type : %s",
508                              pszType);
509                 }
510                 else if( nGeomType == 0 )
511                 {
512                     nGeomType = OGRFromOGCGeomType(pszType);
513                 }
514             }
515             bHasFoundGeomElements = true;
516             auto poDefn = new GMLGeometryPropertyDefn(
517                 pszName, pszElementPath, nGeomType, -1, bNullable);
518             if( AddGeometryProperty(poDefn) < 0 )
519                 delete poDefn;
520             bHasValidGeometryName = false;
521             bHasValidGeometryElementPath = false;
522             bHasFoundGeomType = false;
523         }
524         else if( psThis->eType == CXT_Element &&
525                  strcmp(psThis->pszValue, "GeometryName") == 0 )
526         {
527             bHasFoundGeomElements = true;
528 
529             if( bHasValidGeometryName )
530             {
531                 auto poDefn = new GMLGeometryPropertyDefn(
532                     pszGName, pszGPath, nGeomType, -1, true);
533                 if( AddGeometryProperty(poDefn) < 0 )
534                     delete poDefn;
535                 // bHasValidGeometryName = false;
536                 bHasValidGeometryElementPath = false;
537                 bHasFoundGeomType = false;
538                 pszGPath = "";
539                 nGeomType = wkbUnknown;
540             }
541             pszGName = CPLGetXMLValue(psThis, nullptr, "");
542             bHasValidGeometryName = true;
543         }
544         else if( psThis->eType == CXT_Element &&
545                  strcmp(psThis->pszValue, "GeometryElementPath") == 0 )
546         {
547             bHasFoundGeomElements = true;
548 
549             if( bHasValidGeometryElementPath )
550             {
551                 auto poDefn = new GMLGeometryPropertyDefn(
552                     pszGName, pszGPath, nGeomType, -1, true);
553                 if( AddGeometryProperty(poDefn) < 0 )
554                     delete poDefn;
555                 bHasValidGeometryName = false;
556                 // bHasValidGeometryElementPath = false;
557                 bHasFoundGeomType = false;
558                 pszGName = "";
559                 nGeomType = wkbUnknown;
560             }
561             pszGPath = CPLGetXMLValue(psThis, nullptr, "");
562             bHasValidGeometryElementPath = true;
563         }
564         else if( psThis->eType == CXT_Element &&
565                  strcmp(psThis->pszValue, "GeometryType") == 0 )
566         {
567             bHasFoundGeomElements = true;
568 
569             if( bHasFoundGeomType )
570             {
571                 auto poDefn = new GMLGeometryPropertyDefn(
572                     pszGName, pszGPath, nGeomType, -1, true);
573                 if( AddGeometryProperty(poDefn) < 0 )
574                     delete poDefn;
575                 bHasValidGeometryName = false;
576                 bHasValidGeometryElementPath = false;
577                 // bHasFoundGeomType = false;
578                 pszGName = "";
579                 pszGPath = "";
580             }
581             const char *pszGeometryType = CPLGetXMLValue(psThis, nullptr, nullptr);
582             nGeomType = wkbUnknown;
583             if( pszGeometryType != nullptr && !EQUAL(pszGeometryType, "0") )
584             {
585                 nGeomType = atoi(pszGeometryType);
586                 const int nFlattenGeomType = FlattenGeomTypeFromInt(nGeomType);
587                 if( nGeomType == 100 || EQUAL(pszGeometryType, "NONE") )
588                 {
589                     bHasValidGeometryElementPath = false;
590                     bHasFoundGeomType = false;
591                     break;
592                 }
593                 else if( nGeomType != 0 &&
594                          !(nFlattenGeomType >= static_cast<int>(wkbPoint) &&
595                            nFlattenGeomType <= static_cast<int>(wkbTIN)) )
596                 {
597                     nGeomType = wkbUnknown;
598                     CPLError(CE_Warning, CPLE_AppDefined,
599                              "Unrecognized geometry type : %s",
600                              pszGeometryType);
601                 }
602                 else if( nGeomType == 0 )
603                 {
604                     nGeomType = OGRFromOGCGeomType(pszGeometryType);
605                 }
606             }
607             bHasFoundGeomType = true;
608         }
609     }
610 
611     // If there was a dangling <GeometryElementPath> or <GeometryType> or
612     // that no explicit geometry information has been found, then add
613     // a geometry field.
614     if( bHasValidGeometryElementPath || bHasFoundGeomType ||
615         !bHasFoundGeomElements )
616     {
617         auto poDefn = new GMLGeometryPropertyDefn(pszGName, pszGPath,
618                                                   nGeomType, -1, true);
619         if( AddGeometryProperty(poDefn) < 0 )
620             delete poDefn;
621     }
622 
623     SetSRSName(CPLGetXMLValue(psRoot, "SRSName", nullptr));
624 
625     // Collect dataset specific info.
626     CPLXMLNode *psDSI = CPLGetXMLNode(psRoot, "DatasetSpecificInfo");
627     if( psDSI != nullptr )
628     {
629         const char *pszValue = CPLGetXMLValue(psDSI, "FeatureCount", nullptr);
630         if( pszValue != nullptr )
631             SetFeatureCount(CPLAtoGIntBig(pszValue));
632 
633         // Eventually we should support XML subtrees.
634         pszValue = CPLGetXMLValue(psDSI, "ExtraInfo", nullptr);
635         if( pszValue != nullptr )
636             SetExtraInfo(pszValue);
637 
638         if( CPLGetXMLValue(psDSI, "ExtentXMin", nullptr) != nullptr &&
639             CPLGetXMLValue(psDSI, "ExtentXMax", nullptr) != nullptr &&
640             CPLGetXMLValue(psDSI, "ExtentYMin", nullptr) != nullptr &&
641             CPLGetXMLValue(psDSI, "ExtentYMax", nullptr) != nullptr )
642         {
643             SetExtents(CPLAtof(CPLGetXMLValue(psDSI, "ExtentXMin", "0.0")),
644                        CPLAtof(CPLGetXMLValue(psDSI, "ExtentXMax", "0.0")),
645                        CPLAtof(CPLGetXMLValue(psDSI, "ExtentYMin", "0.0")),
646                        CPLAtof(CPLGetXMLValue(psDSI, "ExtentYMax", "0.0")));
647         }
648     }
649 
650     // Collect property definitions.
651     for( psThis = psRoot->psChild; psThis != nullptr; psThis = psThis->psNext )
652     {
653         if( psThis->eType == CXT_Element &&
654             EQUAL(psThis->pszValue, "PropertyDefn") )
655         {
656             const char *pszName = CPLGetXMLValue(psThis, "Name", nullptr);
657             const char *pszType = CPLGetXMLValue(psThis, "Type", "Untyped");
658             const char *pszSubType = CPLGetXMLValue(psThis, "Subtype", "");
659             const char *pszCondition =
660                 CPLGetXMLValue(psThis, "Condition", nullptr);
661             const bool bNullable =
662                 CPLTestBool(CPLGetXMLValue(psThis, "Nullable", "true"));
663             const bool bUnique =
664                 CPLTestBool(CPLGetXMLValue(psThis, "Unique", "false"));
665 
666             if( pszName == nullptr )
667             {
668                 CPLError(
669                     CE_Failure, CPLE_AppDefined,
670                     "GMLFeatureClass %s has a PropertyDefn without a <Name>.",
671                     m_pszName);
672                 return false;
673             }
674 
675             GMLPropertyDefn *poPDefn = new GMLPropertyDefn(
676                 pszName, CPLGetXMLValue(psThis, "ElementPath", nullptr));
677 
678             poPDefn->SetNullable(bNullable);
679             poPDefn->SetUnique(bUnique);
680             if( EQUAL(pszType, "Untyped") )
681             {
682                 poPDefn->SetType(GMLPT_Untyped);
683             }
684             else if( EQUAL(pszType, "String") )
685             {
686                 if( EQUAL(pszSubType, "Boolean") )
687                 {
688                     poPDefn->SetType(GMLPT_Boolean);
689                     poPDefn->SetWidth(1);
690                 }
691                 else if( EQUAL(pszSubType, "Date") )
692                 {
693                     poPDefn->SetType(GMLPT_Date);
694                 }
695                 else if( EQUAL(pszSubType, "Time") )
696                 {
697                     poPDefn->SetType(GMLPT_Time);
698                 }
699                 else if( EQUAL(pszSubType, "Datetime") )
700                 {
701                     poPDefn->SetType(GMLPT_DateTime);
702                 }
703                 else
704                 {
705                     poPDefn->SetType(GMLPT_String);
706                     poPDefn->SetWidth(
707                         atoi(CPLGetXMLValue(psThis, "Width", "0")));
708                 }
709             }
710             else if( EQUAL(pszType, "Integer") )
711             {
712                 if( EQUAL(pszSubType, "Short") )
713                 {
714                     poPDefn->SetType(GMLPT_Short);
715                 }
716                 else if( EQUAL(pszSubType, "Integer64") )
717                 {
718                     poPDefn->SetType(GMLPT_Integer64);
719                 }
720                 else
721                 {
722                     poPDefn->SetType(GMLPT_Integer);
723                 }
724                 poPDefn->SetWidth(atoi(CPLGetXMLValue(psThis, "Width", "0")));
725             }
726             else if( EQUAL(pszType, "Real") )
727             {
728                 if( EQUAL(pszSubType, "Float") )
729                 {
730                     poPDefn->SetType(GMLPT_Float);
731                 }
732                 else
733                 {
734                     poPDefn->SetType(GMLPT_Real);
735                 }
736                 poPDefn->SetWidth(atoi(CPLGetXMLValue(psThis, "Width", "0")));
737                 poPDefn->SetPrecision(
738                     atoi(CPLGetXMLValue(psThis, "Precision", "0")));
739             }
740             else if( EQUAL(pszType, "StringList") )
741             {
742                 if( EQUAL(pszSubType, "Boolean") )
743                     poPDefn->SetType(GMLPT_BooleanList);
744                 else
745                     poPDefn->SetType(GMLPT_StringList);
746             }
747             else if( EQUAL(pszType, "IntegerList") )
748             {
749                 if( EQUAL(pszSubType, "Integer64") )
750                     poPDefn->SetType(GMLPT_Integer64List);
751                 else
752                     poPDefn->SetType(GMLPT_IntegerList);
753             }
754             else if( EQUAL(pszType, "RealList") )
755             {
756                 poPDefn->SetType(GMLPT_RealList);
757             }
758             else if( EQUAL(pszType, "Complex") ) {
759                 poPDefn->SetType(GMLPT_Complex);
760             }
761             else if( EQUAL(pszType, "FeatureProperty") )
762             {
763                 poPDefn->SetType(GMLPT_FeatureProperty);
764             }
765             else if( EQUAL(pszType, "FeaturePropertyList") )
766             {
767                 poPDefn->SetType(GMLPT_FeaturePropertyList );
768             }
769             else
770             {
771                 CPLError(CE_Failure, CPLE_AppDefined,
772                          "Unrecognized property type (%s) in (%s).",
773                          pszType, pszName);
774                 delete poPDefn;
775                 return false;
776             }
777             if( pszCondition != nullptr )
778                 poPDefn->SetCondition(pszCondition);
779 
780             if( AddProperty(poPDefn) < 0 )
781                 delete poPDefn;
782         }
783     }
784 
785     return true;
786 }
787 
788 /************************************************************************/
789 /*                           SerializeToXML()                           */
790 /************************************************************************/
791 
SerializeToXML()792 CPLXMLNode *GMLFeatureClass::SerializeToXML()
793 
794 {
795     // Set feature class and core information.
796     CPLXMLNode *psRoot = CPLCreateXMLNode(nullptr, CXT_Element, "GMLFeatureClass");
797 
798     CPLCreateXMLElementAndValue(psRoot, "Name", GetName());
799     CPLCreateXMLElementAndValue(psRoot, "ElementPath", GetElementName());
800 
801     if( m_nGeometryPropertyCount > 1 )
802     {
803         for(int i = 0; i < m_nGeometryPropertyCount; i++)
804         {
805             GMLGeometryPropertyDefn *poGeomFDefn = m_papoGeometryProperty[i];
806 
807             CPLXMLNode *psPDefnNode =
808                 CPLCreateXMLNode(psRoot, CXT_Element, "GeomPropertyDefn");
809             if( strlen(poGeomFDefn->GetName()) > 0 )
810                 CPLCreateXMLElementAndValue(psPDefnNode, "Name",
811                                             poGeomFDefn->GetName());
812             if( poGeomFDefn->GetSrcElement() != nullptr &&
813                 strlen(poGeomFDefn->GetSrcElement()) > 0 )
814                 CPLCreateXMLElementAndValue(psPDefnNode, "ElementPath",
815                                             poGeomFDefn->GetSrcElement());
816 
817             if( poGeomFDefn->GetType() != 0 /* wkbUnknown */ )
818             {
819                 char szValue[128] = {};
820 
821                 const OGRwkbGeometryType eType =
822                     static_cast<OGRwkbGeometryType>(poGeomFDefn->GetType());
823 
824                 CPLString osStr(OGRToOGCGeomType(eType));
825                 if( wkbHasZ(eType) )
826                     osStr += "Z";
827                 CPLCreateXMLNode(psPDefnNode, CXT_Comment, osStr.c_str());
828 
829                 snprintf(szValue, sizeof(szValue), "%d", eType);
830                 CPLCreateXMLElementAndValue(psPDefnNode, "Type", szValue);
831             }
832         }
833     }
834     else if( m_nGeometryPropertyCount == 1 )
835     {
836         GMLGeometryPropertyDefn *poGeomFDefn = m_papoGeometryProperty[0];
837 
838         if( strlen(poGeomFDefn->GetName()) > 0 )
839             CPLCreateXMLElementAndValue(psRoot, "GeometryName",
840                                         poGeomFDefn->GetName());
841 
842         if( poGeomFDefn->GetSrcElement() != nullptr &&
843             strlen(poGeomFDefn->GetSrcElement()) > 0 )
844             CPLCreateXMLElementAndValue(psRoot, "GeometryElementPath",
845                                         poGeomFDefn->GetSrcElement());
846 
847         if( poGeomFDefn->GetType() != 0 /* wkbUnknown */ )
848         {
849             char szValue[128] = {};
850 
851             OGRwkbGeometryType eType =
852                 static_cast<OGRwkbGeometryType>(poGeomFDefn->GetType());
853 
854             CPLString osStr(OGRToOGCGeomType(eType));
855             if( wkbHasZ(eType) )
856                 osStr += "Z";
857             CPLCreateXMLNode(psRoot, CXT_Comment, osStr.c_str());
858 
859             snprintf(szValue, sizeof(szValue), "%d", eType);
860             CPLCreateXMLElementAndValue(psRoot, "GeometryType", szValue);
861         }
862     }
863     else
864     {
865         CPLCreateXMLElementAndValue(psRoot, "GeometryType", "100");
866     }
867 
868     const char *pszSRSName = GetSRSName();
869     if( pszSRSName )
870     {
871         CPLCreateXMLElementAndValue(psRoot, "SRSName", pszSRSName);
872     }
873 
874     // Write out dataset specific information.
875     if( m_bHaveExtents || m_nFeatureCount != -1 || m_pszExtraInfo != nullptr )
876     {
877         CPLXMLNode *psDSI =
878             CPLCreateXMLNode(psRoot, CXT_Element, "DatasetSpecificInfo");
879 
880         if( m_nFeatureCount != -1 )
881         {
882             char szValue[128] = {};
883 
884             snprintf(szValue, sizeof(szValue), CPL_FRMT_GIB, m_nFeatureCount);
885             CPLCreateXMLElementAndValue(psDSI, "FeatureCount", szValue);
886         }
887 
888         if( m_bHaveExtents &&
889             fabs(m_dfXMin) < 1e100 &&
890             fabs(m_dfXMax) < 1e100 &&
891             fabs(m_dfYMin) < 1e100 &&
892             fabs(m_dfYMax) < 1e100 )
893         {
894             char szValue[128] = {};
895 
896             CPLsnprintf(szValue, sizeof(szValue), "%.5f", m_dfXMin);
897             CPLCreateXMLElementAndValue(psDSI, "ExtentXMin", szValue);
898 
899             CPLsnprintf(szValue, sizeof(szValue), "%.5f", m_dfXMax);
900             CPLCreateXMLElementAndValue(psDSI, "ExtentXMax", szValue);
901 
902             CPLsnprintf(szValue, sizeof(szValue), "%.5f", m_dfYMin);
903             CPLCreateXMLElementAndValue(psDSI, "ExtentYMin", szValue);
904 
905             CPLsnprintf(szValue, sizeof(szValue), "%.5f", m_dfYMax);
906             CPLCreateXMLElementAndValue(psDSI, "ExtentYMax", szValue);
907         }
908 
909         if( m_pszExtraInfo )
910             CPLCreateXMLElementAndValue(psDSI, "ExtraInfo", m_pszExtraInfo);
911     }
912 
913     CPLXMLNode* psLastChild = psRoot->psChild;
914     while( psLastChild->psNext )
915     {
916         psLastChild = psLastChild->psNext;
917     }
918 
919     // Emit property information.
920     for( int iProperty = 0; iProperty < GetPropertyCount(); iProperty++ )
921     {
922         GMLPropertyDefn *poPDefn = GetProperty(iProperty);
923         const char *pszTypeName = "Unknown";
924 
925         CPLXMLNode *psPDefnNode =
926             CPLCreateXMLNode(nullptr, CXT_Element, "PropertyDefn");
927         psLastChild->psNext = psPDefnNode;
928         psLastChild = psPDefnNode;
929         CPLCreateXMLElementAndValue(psPDefnNode, "Name", poPDefn->GetName());
930         CPLCreateXMLElementAndValue(psPDefnNode, "ElementPath",
931                                     poPDefn->GetSrcElement());
932         const auto gmlType = poPDefn->GetType();
933         const char* pszSubTypeName = nullptr;
934         switch( gmlType )
935         {
936           case GMLPT_Untyped:
937             pszTypeName = "Untyped";
938             break;
939 
940           case GMLPT_String:
941             pszTypeName = "String";
942             break;
943 
944           case GMLPT_Boolean:
945             pszTypeName = "String";
946             pszSubTypeName = "Boolean";
947             break;
948 
949           case GMLPT_Date:
950             pszTypeName = "String";
951             pszSubTypeName = "Date";
952             break;
953 
954           case GMLPT_Time:
955             pszTypeName = "String";
956             pszSubTypeName = "Time";
957             break;
958 
959           case GMLPT_DateTime:
960             pszTypeName = "String";
961             pszSubTypeName = "DateTime";
962             break;
963 
964           case GMLPT_Integer:
965             pszTypeName = "Integer";
966             break;
967 
968           case GMLPT_Short:
969             pszTypeName = "Integer";
970             pszSubTypeName = "Short";
971             break;
972 
973           case GMLPT_Integer64:
974             pszTypeName = "Integer";
975             pszSubTypeName = "Integer64";
976             break;
977 
978           case GMLPT_Real:
979             pszTypeName = "Real";
980             break;
981 
982           case GMLPT_Float:
983             pszTypeName = "Real";
984             pszSubTypeName = "Float";
985             break;
986 
987           case GMLPT_Complex:
988             pszTypeName = "Complex";
989             break;
990 
991           case GMLPT_IntegerList:
992             pszTypeName = "IntegerList";
993             break;
994 
995           case GMLPT_Integer64List:
996             pszTypeName = "IntegerList";
997             pszSubTypeName = "Integer64";
998             break;
999 
1000           case GMLPT_RealList:
1001             pszTypeName = "RealList";
1002             break;
1003 
1004           case GMLPT_StringList:
1005             pszTypeName = "StringList";
1006             break;
1007 
1008           case GMLPT_BooleanList:
1009             pszTypeName = "StringList";
1010             pszSubTypeName = "Boolean";
1011             break;
1012 
1013           // Should not happen in practice for now because this is not
1014           // autodetected.
1015           case GMLPT_FeatureProperty:
1016             pszTypeName = "FeatureProperty";
1017             break;
1018 
1019           // Should not happen in practice for now because this is not
1020           // autodetected.
1021           case GMLPT_FeaturePropertyList:
1022             pszTypeName = "FeaturePropertyList";
1023             break;
1024         }
1025         CPLCreateXMLElementAndValue(psPDefnNode, "Type", pszTypeName);
1026         if( pszSubTypeName )
1027             CPLCreateXMLElementAndValue(psPDefnNode, "Subtype", pszSubTypeName);
1028 
1029         if( EQUAL(pszTypeName, "String") )
1030         {
1031             char szMaxLength[48] = {};
1032             snprintf(szMaxLength, sizeof(szMaxLength), "%d",
1033                      poPDefn->GetWidth());
1034             CPLCreateXMLElementAndValue(psPDefnNode, "Width", szMaxLength);
1035         }
1036         if( poPDefn->GetWidth() > 0 && EQUAL(pszTypeName, "Integer") )
1037         {
1038             char szLength[48] = {};
1039             snprintf(szLength, sizeof(szLength), "%d", poPDefn->GetWidth());
1040             CPLCreateXMLElementAndValue(psPDefnNode, "Width", szLength);
1041         }
1042         if( poPDefn->GetWidth() > 0 && EQUAL(pszTypeName, "Real") )
1043         {
1044             char szLength[48] = {};
1045             snprintf(szLength, sizeof(szLength), "%d", poPDefn->GetWidth());
1046             CPLCreateXMLElementAndValue(psPDefnNode, "Width", szLength);
1047             char szPrecision[48] = {};
1048             snprintf(szPrecision, sizeof(szPrecision), "%d",
1049                      poPDefn->GetPrecision());
1050             CPLCreateXMLElementAndValue(psPDefnNode, "Precision", szPrecision);
1051         }
1052     }
1053 
1054     return psRoot;
1055 }
1056 
1057 /************************************************************************/
1058 /*                       GML_GetOGRFieldType()                          */
1059 /************************************************************************/
1060 
GML_GetOGRFieldType(GMLPropertyType eType,OGRFieldSubType & eSubType)1061 OGRFieldType GML_GetOGRFieldType(GMLPropertyType eType, OGRFieldSubType& eSubType)
1062 {
1063     OGRFieldType eFType = OFTString;
1064     eSubType = OFSTNone;
1065     if( eType == GMLPT_Untyped )
1066         eFType = OFTString;
1067     else if( eType == GMLPT_String )
1068         eFType = OFTString;
1069     else if( eType == GMLPT_Integer )
1070         eFType = OFTInteger;
1071     else if( eType == GMLPT_Boolean )
1072     {
1073         eFType = OFTInteger;
1074         eSubType = OFSTBoolean;
1075     }
1076     else if( eType == GMLPT_Short )
1077     {
1078         eFType = OFTInteger;
1079         eSubType = OFSTInt16;
1080     }
1081     else if( eType == GMLPT_Integer64 )
1082         eFType = OFTInteger64;
1083     else if( eType == GMLPT_Real )
1084         eFType = OFTReal;
1085     else if( eType == GMLPT_Float )
1086     {
1087         eFType = OFTReal;
1088         eSubType = OFSTFloat32;
1089     }
1090     else if( eType == GMLPT_StringList )
1091         eFType = OFTStringList;
1092     else if( eType == GMLPT_IntegerList )
1093         eFType = OFTIntegerList;
1094     else if( eType == GMLPT_BooleanList )
1095     {
1096         eFType = OFTIntegerList;
1097         eSubType = OFSTBoolean;
1098     }
1099     else if( eType == GMLPT_Integer64List )
1100         eFType = OFTInteger64List;
1101     else if( eType == GMLPT_RealList )
1102         eFType = OFTRealList;
1103     else if( eType == GMLPT_Date)
1104         eFType = OFTDate;
1105     else if( eType == GMLPT_Time )
1106         eFType = OFTTime;
1107     else if( eType == GMLPT_DateTime )
1108         eFType = OFTDateTime;
1109     else if( eType == GMLPT_FeaturePropertyList )
1110         eFType = OFTStringList;
1111     return eFType;
1112 }
1113