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