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