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