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 840 : GMLFeatureClass::GMLFeatureClass(const char *pszName)
37 840 : : m_pszName(CPLStrdup(pszName)), m_pszElementName(nullptr),
38 840 : 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 840 : m_bSRSNameConsistent(true)
45 : {
46 840 : }
47 :
48 : /************************************************************************/
49 : /* ~GMLFeatureClass() */
50 : /************************************************************************/
51 :
52 840 : GMLFeatureClass::~GMLFeatureClass()
53 :
54 : {
55 840 : CPLFree(m_pszName);
56 840 : CPLFree(m_pszElementName);
57 :
58 3693 : for (int i = 0; i < m_nPropertyCount; i++)
59 2853 : delete m_papoProperty[i];
60 840 : CPLFree(m_papoProperty);
61 :
62 840 : ClearGeometryProperties();
63 :
64 840 : CPLFree(m_pszSRSName);
65 840 : }
66 :
67 : /************************************************************************/
68 : /* StealProperties() */
69 : /************************************************************************/
70 :
71 2 : void GMLFeatureClass::StealProperties()
72 : {
73 2 : m_nPropertyCount = 0;
74 2 : CPLFree(m_papoProperty);
75 2 : m_papoProperty = nullptr;
76 2 : m_oMapPropertyNameToIndex.clear();
77 2 : m_oMapPropertySrcElementToIndex.clear();
78 2 : }
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 29920 : GMLPropertyDefn *GMLFeatureClass::GetProperty(int iIndex) const
106 :
107 : {
108 29920 : if (iIndex < 0 || iIndex >= m_nPropertyCount)
109 3263 : return nullptr;
110 :
111 26657 : return m_papoProperty[iIndex];
112 : }
113 :
114 : /************************************************************************/
115 : /* GetPropertyIndex() */
116 : /************************************************************************/
117 :
118 3362 : int GMLFeatureClass::GetPropertyIndex(const char *pszName) const
119 :
120 : {
121 3362 : auto oIter = m_oMapPropertyNameToIndex.find(CPLString(pszName).toupper());
122 3362 : if (oIter != m_oMapPropertyNameToIndex.end())
123 15 : return oIter->second;
124 :
125 3347 : return -1;
126 : }
127 :
128 : /************************************************************************/
129 : /* GetPropertyIndexBySrcElement() */
130 : /************************************************************************/
131 :
132 5333 : int GMLFeatureClass::GetPropertyIndexBySrcElement(const char *pszElement,
133 : int nLen) const
134 :
135 : {
136 : auto oIter =
137 5333 : m_oMapPropertySrcElementToIndex.find(CPLString(pszElement, nLen));
138 5333 : if (oIter != m_oMapPropertySrcElementToIndex.end())
139 2787 : return oIter->second;
140 :
141 2546 : return -1;
142 : }
143 :
144 : /************************************************************************/
145 : /* AddProperty() */
146 : /************************************************************************/
147 :
148 2867 : int GMLFeatureClass::AddProperty(GMLPropertyDefn *poDefn, int iPos)
149 :
150 : {
151 2867 : 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 2865 : m_nPropertyCount++;
161 2865 : m_papoProperty = static_cast<GMLPropertyDefn **>(
162 2865 : CPLRealloc(m_papoProperty, sizeof(void *) * m_nPropertyCount));
163 :
164 2865 : if (iPos < 0)
165 : {
166 2862 : 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 2865 : m_papoProperty[iPos] = poDefn;
185 2865 : m_oMapPropertyNameToIndex[CPLString(poDefn->GetName()).toupper()] = iPos;
186 2865 : if (m_oMapPropertySrcElementToIndex.find(poDefn->GetSrcElement()) ==
187 5730 : m_oMapPropertySrcElementToIndex.end())
188 : {
189 2863 : m_oMapPropertySrcElementToIndex[poDefn->GetSrcElement()] = iPos;
190 : }
191 :
192 2865 : return iPos;
193 : }
194 :
195 : /************************************************************************/
196 : /* GetGeometryProperty(int) */
197 : /************************************************************************/
198 :
199 2872 : GMLGeometryPropertyDefn *GMLFeatureClass::GetGeometryProperty(int iIndex) const
200 : {
201 2872 : if (iIndex < 0 || iIndex >= m_nGeometryPropertyCount)
202 0 : return nullptr;
203 :
204 2872 : return m_papoGeometryProperty[iIndex];
205 : }
206 :
207 : /************************************************************************/
208 : /* GetGeometryPropertyIndexBySrcElement() */
209 : /************************************************************************/
210 :
211 2077 : int GMLFeatureClass::GetGeometryPropertyIndexBySrcElement(
212 : const char *pszElement) const
213 :
214 : {
215 2352 : for (int i = 0; i < m_nGeometryPropertyCount; i++)
216 1354 : if (strcmp(pszElement, m_papoGeometryProperty[i]->GetSrcElement()) == 0)
217 1079 : return i;
218 :
219 998 : return -1;
220 : }
221 :
222 : /************************************************************************/
223 : /* AddGeometryProperty() */
224 : /************************************************************************/
225 :
226 868 : int GMLFeatureClass::AddGeometryProperty(GMLGeometryPropertyDefn *poDefn)
227 :
228 : {
229 868 : 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 868 : m_nGeometryPropertyCount++;
239 1736 : m_papoGeometryProperty = static_cast<GMLGeometryPropertyDefn **>(CPLRealloc(
240 868 : m_papoGeometryProperty, sizeof(void *) * m_nGeometryPropertyCount));
241 :
242 868 : m_papoGeometryProperty[m_nGeometryPropertyCount - 1] = poDefn;
243 :
244 868 : return m_nGeometryPropertyCount - 1;
245 : }
246 :
247 : /************************************************************************/
248 : /* ClearGeometryProperties() */
249 : /************************************************************************/
250 :
251 840 : void GMLFeatureClass::ClearGeometryProperties()
252 : {
253 1704 : for (int i = 0; i < m_nGeometryPropertyCount; i++)
254 864 : delete m_papoGeometryProperty[i];
255 840 : CPLFree(m_papoGeometryProperty);
256 840 : m_nGeometryPropertyCount = 0;
257 840 : m_papoGeometryProperty = nullptr;
258 840 : }
259 :
260 : /************************************************************************/
261 : /* HasFeatureProperties() */
262 : /************************************************************************/
263 :
264 996 : bool GMLFeatureClass::HasFeatureProperties()
265 : {
266 3773 : for (int i = 0; i < m_nPropertyCount; i++)
267 : {
268 5568 : if (m_papoProperty[i]->GetType() == GMLPT_FeatureProperty ||
269 2782 : m_papoProperty[i]->GetType() == GMLPT_FeaturePropertyList)
270 9 : return true;
271 : }
272 987 : return false;
273 : }
274 :
275 : /************************************************************************/
276 : /* SetElementName() */
277 : /************************************************************************/
278 :
279 150 : void GMLFeatureClass::SetElementName(const char *pszElementName)
280 :
281 : {
282 150 : CPLFree(m_pszElementName);
283 150 : m_pszElementName = CPLStrdup(pszElementName);
284 150 : n_nElementNameLen = static_cast<int>(strlen(pszElementName));
285 150 : }
286 :
287 : /************************************************************************/
288 : /* GetElementName() */
289 : /************************************************************************/
290 :
291 6993 : const char *GMLFeatureClass::GetElementName() const
292 :
293 : {
294 6993 : if (m_pszElementName == nullptr)
295 5159 : return m_pszName;
296 :
297 1834 : return m_pszElementName;
298 : }
299 :
300 : /************************************************************************/
301 : /* GetElementName() */
302 : /************************************************************************/
303 :
304 26205 : size_t GMLFeatureClass::GetElementNameLen() const
305 :
306 : {
307 26205 : if (m_pszElementName == nullptr)
308 11102 : return n_nNameLen;
309 :
310 15103 : return n_nElementNameLen;
311 : }
312 :
313 : /************************************************************************/
314 : /* GetFeatureCount() */
315 : /************************************************************************/
316 :
317 1503 : GIntBig GMLFeatureClass::GetFeatureCount()
318 : {
319 1503 : return m_nFeatureCount;
320 : }
321 :
322 : /************************************************************************/
323 : /* SetFeatureCount() */
324 : /************************************************************************/
325 :
326 707 : void GMLFeatureClass::SetFeatureCount(GIntBig nNewCount)
327 :
328 : {
329 707 : m_nFeatureCount = nNewCount;
330 707 : }
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 543 : void GMLFeatureClass::SetExtents(double dfXMin, double dfXMax, double dfYMin,
360 : double dfYMax)
361 :
362 : {
363 543 : m_dfXMin = dfXMin;
364 543 : m_dfXMax = dfXMax;
365 543 : m_dfYMin = dfYMin;
366 543 : m_dfYMax = dfYMax;
367 :
368 543 : m_bHaveExtents = true;
369 543 : }
370 :
371 : /************************************************************************/
372 : /* GetExtents() */
373 : /************************************************************************/
374 :
375 474 : bool GMLFeatureClass::GetExtents(double *pdfXMin, double *pdfXMax,
376 : double *pdfYMin, double *pdfYMax)
377 :
378 : {
379 474 : if (m_bHaveExtents)
380 : {
381 371 : *pdfXMin = m_dfXMin;
382 371 : *pdfXMax = m_dfXMax;
383 371 : *pdfYMin = m_dfYMin;
384 371 : *pdfYMax = m_dfYMax;
385 : }
386 :
387 474 : return m_bHaveExtents;
388 : }
389 :
390 : /************************************************************************/
391 : /* SetSRSName() */
392 : /************************************************************************/
393 :
394 147 : void GMLFeatureClass::SetSRSName(const char *pszSRSName)
395 :
396 : {
397 147 : m_bSRSNameConsistent = true;
398 147 : CPLFree(m_pszSRSName);
399 147 : m_pszSRSName = pszSRSName ? CPLStrdup(pszSRSName) : nullptr;
400 147 : }
401 :
402 : /************************************************************************/
403 : /* MergeSRSName() */
404 : /************************************************************************/
405 :
406 194 : void GMLFeatureClass::MergeSRSName(const char *pszSRSName)
407 :
408 : {
409 194 : if (!m_bSRSNameConsistent)
410 0 : return;
411 :
412 194 : if (m_pszSRSName == nullptr)
413 : {
414 91 : if (pszSRSName)
415 30 : m_pszSRSName = CPLStrdup(pszSRSName);
416 : }
417 : else
418 : {
419 103 : m_bSRSNameConsistent =
420 103 : pszSRSName != nullptr && strcmp(m_pszSRSName, pszSRSName) == 0;
421 103 : if (!m_bSRSNameConsistent)
422 : {
423 1 : CPLFree(m_pszSRSName);
424 1 : m_pszSRSName = nullptr;
425 : }
426 : }
427 : }
428 :
429 : /************************************************************************/
430 : /* InitializeFromXML() */
431 : /************************************************************************/
432 :
433 101 : bool GMLFeatureClass::InitializeFromXML(CPLXMLNode *psRoot)
434 :
435 : {
436 : // Do some rudimentary checking that this is a well formed node.
437 101 : if (psRoot == nullptr || psRoot->eType != CXT_Element ||
438 101 : !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 101 : 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 101 : CPLFree(m_pszName);
455 101 : m_pszName = CPLStrdup(CPLGetXMLValue(psRoot, "Name", nullptr));
456 101 : n_nNameLen = static_cast<int>(strlen(m_pszName));
457 :
458 101 : SetElementName(CPLGetXMLValue(psRoot, "ElementPath", m_pszName));
459 :
460 : // Collect geometry properties.
461 101 : bool bHasValidGeometryName = false;
462 101 : bool bHasValidGeometryElementPath = false;
463 101 : bool bHasFoundGeomType = false;
464 101 : bool bHasFoundGeomElements = false;
465 101 : const char *pszGName = "";
466 101 : const char *pszGPath = "";
467 101 : OGRwkbGeometryType nGeomType = wkbUnknown;
468 :
469 129 : const auto FlattenGeomTypeFromInt = [](int eType)
470 : {
471 129 : eType = eType & (~wkb25DBitInternalUse);
472 129 : if (eType >= 1000 && eType < 2000) // ISO Z.
473 0 : return eType - 1000;
474 129 : if (eType >= 2000 && eType < 3000) // ISO M.
475 0 : return eType - 2000;
476 129 : if (eType >= 3000 && eType < 4000) // ISO ZM.
477 0 : return eType - 3000;
478 129 : return eType;
479 : };
480 :
481 101 : CPLXMLNode *psThis = nullptr;
482 1619 : for (psThis = psRoot->psChild; psThis != nullptr; psThis = psThis->psNext)
483 : {
484 1530 : if (psThis->eType == CXT_Element &&
485 1106 : 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 1451 : else if (psThis->eType == CXT_Element &&
525 1027 : strcmp(psThis->pszValue, "GeometryName") == 0)
526 : {
527 35 : bHasFoundGeomElements = true;
528 :
529 35 : 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 35 : pszGName = CPLGetXMLValue(psThis, nullptr, "");
542 35 : bHasValidGeometryName = true;
543 : }
544 1416 : else if (psThis->eType == CXT_Element &&
545 992 : strcmp(psThis->pszValue, "GeometryElementPath") == 0)
546 : {
547 41 : bHasFoundGeomElements = true;
548 :
549 41 : 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 41 : pszGPath = CPLGetXMLValue(psThis, nullptr, "");
562 41 : bHasValidGeometryElementPath = true;
563 : }
564 1375 : else if (psThis->eType == CXT_Element &&
565 951 : strcmp(psThis->pszValue, "GeometryType") == 0)
566 : {
567 52 : bHasFoundGeomElements = true;
568 :
569 52 : 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 52 : CPLGetXMLValue(psThis, nullptr, nullptr);
583 52 : nGeomType = wkbUnknown;
584 52 : if (pszGeometryType != nullptr && !EQUAL(pszGeometryType, "0"))
585 : {
586 52 : const int nGeomTypeInt = atoi(pszGeometryType);
587 : const int nFlattenGeomTypeInt =
588 52 : FlattenGeomTypeFromInt(nGeomTypeInt);
589 52 : if (nGeomTypeInt == 100 || EQUAL(pszGeometryType, "NONE"))
590 : {
591 12 : bHasValidGeometryElementPath = false;
592 12 : bHasFoundGeomType = false;
593 12 : break;
594 : }
595 40 : else if (nGeomTypeInt != 0 &&
596 36 : !(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 40 : else if (nGeomTypeInt == 0)
604 : {
605 4 : nGeomType = OGRFromOGCGeomType(pszGeometryType);
606 : }
607 : else
608 : {
609 36 : nGeomType = static_cast<OGRwkbGeometryType>(nGeomTypeInt);
610 : }
611 : }
612 40 : 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 101 : if (bHasValidGeometryElementPath || bHasFoundGeomType ||
620 58 : !bHasFoundGeomElements)
621 : {
622 : auto poDefn = new GMLGeometryPropertyDefn(pszGName, pszGPath, nGeomType,
623 55 : -1, true);
624 55 : if (AddGeometryProperty(poDefn) < 0)
625 0 : delete poDefn;
626 : }
627 :
628 101 : SetSRSName(CPLGetXMLValue(psRoot, "SRSName", nullptr));
629 :
630 : // Collect dataset specific info.
631 101 : CPLXMLNode *psDSI = CPLGetXMLNode(psRoot, "DatasetSpecificInfo");
632 101 : if (psDSI != nullptr)
633 : {
634 54 : const char *pszValue = CPLGetXMLValue(psDSI, "FeatureCount", nullptr);
635 54 : if (pszValue != nullptr)
636 54 : SetFeatureCount(CPLAtoGIntBig(pszValue));
637 :
638 : // Eventually we should support XML subtrees.
639 54 : pszValue = CPLGetXMLValue(psDSI, "ExtraInfo", nullptr);
640 54 : if (pszValue != nullptr)
641 0 : SetExtraInfo(pszValue);
642 :
643 54 : if (CPLGetXMLValue(psDSI, "ExtentXMin", nullptr) != nullptr &&
644 48 : CPLGetXMLValue(psDSI, "ExtentXMax", nullptr) != nullptr &&
645 150 : CPLGetXMLValue(psDSI, "ExtentYMin", nullptr) != nullptr &&
646 48 : CPLGetXMLValue(psDSI, "ExtentYMax", nullptr) != nullptr)
647 : {
648 48 : 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 1679 : for (psThis = psRoot->psChild; psThis != nullptr; psThis = psThis->psNext)
657 : {
658 1578 : if (psThis->eType == CXT_Element &&
659 1154 : EQUAL(psThis->pszValue, "PropertyDefn"))
660 : {
661 641 : const char *pszName = CPLGetXMLValue(psThis, "Name", nullptr);
662 641 : const char *pszType = CPLGetXMLValue(psThis, "Type", "Untyped");
663 641 : const char *pszSubType = CPLGetXMLValue(psThis, "Subtype", "");
664 : const char *pszCondition =
665 641 : CPLGetXMLValue(psThis, "Condition", nullptr);
666 : const bool bNullable =
667 641 : CPLTestBool(CPLGetXMLValue(psThis, "Nullable", "true"));
668 : const bool bUnique =
669 641 : CPLTestBool(CPLGetXMLValue(psThis, "Unique", "false"));
670 641 : const char *pszComment = CPLGetXMLValue(psThis, "Comment", nullptr);
671 :
672 641 : 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 641 : pszName, CPLGetXMLValue(psThis, "ElementPath", nullptr));
683 :
684 641 : poPDefn->SetNullable(bNullable);
685 641 : poPDefn->SetUnique(bUnique);
686 641 : if (EQUAL(pszType, "Untyped"))
687 : {
688 0 : poPDefn->SetType(GMLPT_Untyped);
689 : }
690 641 : 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 289 : else if (EQUAL(pszType, "Integer"))
717 : {
718 189 : if (EQUAL(pszSubType, "Short"))
719 : {
720 0 : poPDefn->SetType(GMLPT_Short);
721 : }
722 189 : else if (EQUAL(pszSubType, "Integer64"))
723 : {
724 60 : poPDefn->SetType(GMLPT_Integer64);
725 : }
726 : else
727 : {
728 129 : poPDefn->SetType(GMLPT_Integer);
729 : }
730 189 : 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 641 : if (pszCondition != nullptr)
785 4 : poPDefn->SetCondition(pszCondition);
786 641 : if (pszComment != nullptr)
787 1 : poPDefn->SetDocumentation(pszComment);
788 :
789 641 : if (AddProperty(poPDefn) < 0)
790 0 : delete poPDefn;
791 : }
792 : }
793 :
794 101 : return true;
795 : }
796 :
797 : /************************************************************************/
798 : /* SerializeToXML() */
799 : /************************************************************************/
800 :
801 108 : CPLXMLNode *GMLFeatureClass::SerializeToXML()
802 :
803 : {
804 : // Set feature class and core information.
805 : CPLXMLNode *psRoot =
806 108 : CPLCreateXMLNode(nullptr, CXT_Element, "GMLFeatureClass");
807 :
808 108 : CPLCreateXMLElementAndValue(psRoot, "Name", GetName());
809 108 : CPLCreateXMLElementAndValue(psRoot, "ElementPath", GetElementName());
810 :
811 108 : 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 106 : else if (m_nGeometryPropertyCount == 1)
845 : {
846 86 : GMLGeometryPropertyDefn *poGeomFDefn = m_papoGeometryProperty[0];
847 :
848 86 : if (strlen(poGeomFDefn->GetName()) > 0)
849 65 : CPLCreateXMLElementAndValue(psRoot, "GeometryName",
850 : poGeomFDefn->GetName());
851 :
852 172 : if (poGeomFDefn->GetSrcElement() != nullptr &&
853 86 : strlen(poGeomFDefn->GetSrcElement()) > 0)
854 65 : CPLCreateXMLElementAndValue(psRoot, "GeometryElementPath",
855 : poGeomFDefn->GetSrcElement());
856 :
857 86 : if (poGeomFDefn->GetType() != 0 /* wkbUnknown */)
858 : {
859 78 : char szValue[128] = {};
860 :
861 : OGRwkbGeometryType eType =
862 78 : static_cast<OGRwkbGeometryType>(poGeomFDefn->GetType());
863 :
864 156 : CPLString osStr(OGRToOGCGeomType(eType));
865 78 : if (wkbHasZ(eType))
866 7 : osStr += "Z";
867 78 : CPLCreateXMLNode(psRoot, CXT_Comment, osStr.c_str());
868 :
869 78 : snprintf(szValue, sizeof(szValue), "%d", eType);
870 78 : CPLCreateXMLElementAndValue(psRoot, "GeometryType", szValue);
871 : }
872 : }
873 : else
874 : {
875 20 : CPLCreateXMLElementAndValue(psRoot, "GeometryType", "100");
876 : }
877 :
878 108 : const char *pszSRSName = GetSRSName();
879 108 : if (pszSRSName)
880 : {
881 54 : CPLCreateXMLElementAndValue(psRoot, "SRSName", pszSRSName);
882 : }
883 :
884 : // Write out dataset specific information.
885 108 : if (m_bHaveExtents || m_nFeatureCount != -1 || m_pszExtraInfo != nullptr)
886 : {
887 : CPLXMLNode *psDSI =
888 107 : CPLCreateXMLNode(psRoot, CXT_Element, "DatasetSpecificInfo");
889 :
890 107 : if (m_nFeatureCount != -1)
891 : {
892 107 : char szValue[128] = {};
893 :
894 107 : snprintf(szValue, sizeof(szValue), CPL_FRMT_GIB, m_nFeatureCount);
895 107 : CPLCreateXMLElementAndValue(psDSI, "FeatureCount", szValue);
896 : }
897 :
898 107 : if (m_bHaveExtents && fabs(m_dfXMin) < 1e100 &&
899 85 : fabs(m_dfXMax) < 1e100 && fabs(m_dfYMin) < 1e100 &&
900 85 : fabs(m_dfYMax) < 1e100)
901 : {
902 85 : char szValue[128] = {};
903 :
904 85 : CPLsnprintf(szValue, sizeof(szValue), "%.5f", m_dfXMin);
905 85 : CPLCreateXMLElementAndValue(psDSI, "ExtentXMin", szValue);
906 :
907 85 : CPLsnprintf(szValue, sizeof(szValue), "%.5f", m_dfXMax);
908 85 : CPLCreateXMLElementAndValue(psDSI, "ExtentXMax", szValue);
909 :
910 85 : CPLsnprintf(szValue, sizeof(szValue), "%.5f", m_dfYMin);
911 85 : CPLCreateXMLElementAndValue(psDSI, "ExtentYMin", szValue);
912 :
913 85 : CPLsnprintf(szValue, sizeof(szValue), "%.5f", m_dfYMax);
914 85 : CPLCreateXMLElementAndValue(psDSI, "ExtentYMax", szValue);
915 : }
916 :
917 107 : if (m_pszExtraInfo)
918 0 : CPLCreateXMLElementAndValue(psDSI, "ExtraInfo", m_pszExtraInfo);
919 : }
920 :
921 108 : CPLXMLNode *psLastChild = psRoot->psChild;
922 687 : while (psLastChild->psNext)
923 : {
924 579 : psLastChild = psLastChild->psNext;
925 : }
926 :
927 : // Emit property information.
928 513 : for (int iProperty = 0; iProperty < GetPropertyCount(); iProperty++)
929 : {
930 405 : GMLPropertyDefn *poPDefn = GetProperty(iProperty);
931 405 : const char *pszTypeName = "Unknown";
932 :
933 : CPLXMLNode *psPDefnNode =
934 405 : CPLCreateXMLNode(nullptr, CXT_Element, "PropertyDefn");
935 405 : psLastChild->psNext = psPDefnNode;
936 405 : psLastChild = psPDefnNode;
937 405 : CPLCreateXMLElementAndValue(psPDefnNode, "Name", poPDefn->GetName());
938 405 : CPLCreateXMLElementAndValue(psPDefnNode, "ElementPath",
939 : poPDefn->GetSrcElement());
940 405 : const auto gmlType = poPDefn->GetType();
941 405 : const char *pszSubTypeName = nullptr;
942 405 : switch (gmlType)
943 : {
944 4 : case GMLPT_Untyped:
945 4 : pszTypeName = "Untyped";
946 4 : break;
947 :
948 249 : case GMLPT_String:
949 249 : pszTypeName = "String";
950 249 : break;
951 :
952 10 : case GMLPT_Boolean:
953 10 : pszTypeName = "String";
954 10 : pszSubTypeName = "Boolean";
955 10 : break;
956 :
957 0 : case GMLPT_Date:
958 0 : pszTypeName = "String";
959 0 : pszSubTypeName = "Date";
960 0 : break;
961 :
962 0 : case GMLPT_Time:
963 0 : pszTypeName = "String";
964 0 : pszSubTypeName = "Time";
965 0 : break;
966 :
967 0 : case GMLPT_DateTime:
968 0 : pszTypeName = "String";
969 0 : pszSubTypeName = "DateTime";
970 0 : break;
971 :
972 72 : case GMLPT_Integer:
973 72 : pszTypeName = "Integer";
974 72 : break;
975 :
976 0 : case GMLPT_Short:
977 0 : pszTypeName = "Integer";
978 0 : pszSubTypeName = "Short";
979 0 : break;
980 :
981 1 : case GMLPT_Integer64:
982 1 : pszTypeName = "Integer";
983 1 : pszSubTypeName = "Integer64";
984 1 : break;
985 :
986 24 : case GMLPT_Real:
987 24 : pszTypeName = "Real";
988 24 : break;
989 :
990 0 : case GMLPT_Float:
991 0 : pszTypeName = "Real";
992 0 : pszSubTypeName = "Float";
993 0 : break;
994 :
995 0 : case GMLPT_Complex:
996 0 : pszTypeName = "Complex";
997 0 : break;
998 :
999 2 : case GMLPT_IntegerList:
1000 2 : pszTypeName = "IntegerList";
1001 2 : break;
1002 :
1003 1 : case GMLPT_Integer64List:
1004 1 : pszTypeName = "IntegerList";
1005 1 : pszSubTypeName = "Integer64";
1006 1 : break;
1007 :
1008 2 : case GMLPT_RealList:
1009 2 : pszTypeName = "RealList";
1010 2 : break;
1011 :
1012 39 : case GMLPT_StringList:
1013 39 : pszTypeName = "StringList";
1014 39 : break;
1015 :
1016 1 : case GMLPT_BooleanList:
1017 1 : pszTypeName = "StringList";
1018 1 : pszSubTypeName = "Boolean";
1019 1 : break;
1020 :
1021 : // Should not happen in practice for now because this is not
1022 : // autodetected.
1023 0 : case GMLPT_FeatureProperty:
1024 0 : pszTypeName = "FeatureProperty";
1025 0 : break;
1026 :
1027 : // Should not happen in practice for now because this is not
1028 : // autodetected.
1029 0 : case GMLPT_FeaturePropertyList:
1030 0 : pszTypeName = "FeaturePropertyList";
1031 0 : break;
1032 : }
1033 405 : CPLCreateXMLElementAndValue(psPDefnNode, "Type", pszTypeName);
1034 405 : if (pszSubTypeName)
1035 13 : CPLCreateXMLElementAndValue(psPDefnNode, "Subtype", pszSubTypeName);
1036 :
1037 405 : if (EQUAL(pszTypeName, "String"))
1038 : {
1039 259 : char szMaxLength[48] = {};
1040 259 : snprintf(szMaxLength, sizeof(szMaxLength), "%d",
1041 : poPDefn->GetWidth());
1042 259 : CPLCreateXMLElementAndValue(psPDefnNode, "Width", szMaxLength);
1043 : }
1044 405 : if (poPDefn->GetWidth() > 0 && EQUAL(pszTypeName, "Integer"))
1045 : {
1046 0 : char szLength[48] = {};
1047 0 : snprintf(szLength, sizeof(szLength), "%d", poPDefn->GetWidth());
1048 0 : CPLCreateXMLElementAndValue(psPDefnNode, "Width", szLength);
1049 : }
1050 405 : if (poPDefn->GetWidth() > 0 && EQUAL(pszTypeName, "Real"))
1051 : {
1052 0 : char szLength[48] = {};
1053 0 : snprintf(szLength, sizeof(szLength), "%d", poPDefn->GetWidth());
1054 0 : CPLCreateXMLElementAndValue(psPDefnNode, "Width", szLength);
1055 0 : char szPrecision[48] = {};
1056 0 : snprintf(szPrecision, sizeof(szPrecision), "%d",
1057 : poPDefn->GetPrecision());
1058 0 : CPLCreateXMLElementAndValue(psPDefnNode, "Precision", szPrecision);
1059 : }
1060 405 : if (!poPDefn->GetDocumentation().empty())
1061 : {
1062 0 : CPLCreateXMLElementAndValue(psPDefnNode, "Comment",
1063 0 : poPDefn->GetDocumentation().c_str());
1064 : }
1065 : }
1066 :
1067 108 : return psRoot;
1068 : }
1069 :
1070 : /************************************************************************/
1071 : /* GML_GetOGRFieldType() */
1072 : /************************************************************************/
1073 :
1074 2591 : OGRFieldType GML_GetOGRFieldType(GMLPropertyType eType,
1075 : OGRFieldSubType &eSubType)
1076 : {
1077 2591 : OGRFieldType eFType = OFTString;
1078 2591 : eSubType = OFSTNone;
1079 2591 : if (eType == GMLPT_Untyped)
1080 0 : eFType = OFTString;
1081 2591 : else if (eType == GMLPT_String)
1082 1194 : eFType = OFTString;
1083 1397 : else if (eType == GMLPT_Integer)
1084 363 : eFType = OFTInteger;
1085 1034 : else if (eType == GMLPT_Boolean)
1086 : {
1087 124 : eFType = OFTInteger;
1088 124 : eSubType = OFSTBoolean;
1089 : }
1090 910 : else if (eType == GMLPT_Short)
1091 : {
1092 117 : eFType = OFTInteger;
1093 117 : eSubType = OFSTInt16;
1094 : }
1095 793 : else if (eType == GMLPT_Integer64)
1096 78 : eFType = OFTInteger64;
1097 715 : else if (eType == GMLPT_Real)
1098 249 : 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 2591 : return eFType;
1126 : }
|