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