Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GML Reader
4 : * Purpose: Implementation of GMLReader class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2002, Frank Warmerdam
9 : * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "gmlreaderp.h"
16 : #include "gmlreader.h"
17 :
18 : #include <climits>
19 : #include <cstdlib>
20 : #include <cstring>
21 : #include <algorithm>
22 : #include <set>
23 : #include <string>
24 :
25 : #include "cpl_conv.h"
26 : #include "cpl_error.h"
27 : #include "cpl_multiproc.h"
28 : #include "cpl_string.h"
29 : #include "cpl_vsi_virtual.h"
30 : #include "gmlutils.h"
31 : #include "ogr_geometry.h"
32 :
33 : #ifdef EMBED_RESOURCE_FILES
34 : #include "embedded_resources.h"
35 : #endif
36 :
37 : /************************************************************************/
38 : /* ~IGMLReader() */
39 : /************************************************************************/
40 :
41 436 : IGMLReader::~IGMLReader()
42 : {
43 436 : }
44 :
45 : /************************************************************************/
46 : /* ==================================================================== */
47 : /* No XERCES or EXPAT Library */
48 : /* ==================================================================== */
49 : /************************************************************************/
50 : #if !defined(HAVE_XERCES) && !defined(HAVE_EXPAT)
51 :
52 : /************************************************************************/
53 : /* CreateGMLReader() */
54 : /************************************************************************/
55 :
56 : IGMLReader *CreateGMLReader(bool /*bUseExpatParserPreferably*/,
57 : bool /*bInvertAxisOrderIfLatLong*/,
58 : bool /*bConsiderEPSGAsURN*/,
59 : GMLSwapCoordinatesEnum /* eSwapCoordinates */,
60 : bool /*bGetSecondaryGeometryOption*/)
61 : {
62 : CPLError(CE_Failure, CPLE_AppDefined,
63 : "Unable to create Xerces C++ or Expat based GML reader, Xerces "
64 : "or Expat support not configured into GDAL/OGR.");
65 : return nullptr;
66 : }
67 :
68 : /************************************************************************/
69 : /* ==================================================================== */
70 : /* With XERCES or EXPAT Library */
71 : /* ==================================================================== */
72 : /************************************************************************/
73 : #else /* defined(HAVE_XERCES) || defined(HAVE_EXPAT) */
74 :
75 : /************************************************************************/
76 : /* CreateGMLReader() */
77 : /************************************************************************/
78 :
79 431 : IGMLReader *CreateGMLReader(bool bUseExpatParserPreferably,
80 : bool bInvertAxisOrderIfLatLong,
81 : bool bConsiderEPSGAsURN,
82 : GMLSwapCoordinatesEnum eSwapCoordinates,
83 : bool bGetSecondaryGeometryOption)
84 :
85 : {
86 : return new GMLReader(bUseExpatParserPreferably, bInvertAxisOrderIfLatLong,
87 : bConsiderEPSGAsURN, eSwapCoordinates,
88 431 : bGetSecondaryGeometryOption);
89 : }
90 :
91 : #endif
92 :
93 : CPLMutex *GMLReader::hMutex = nullptr;
94 :
95 : /************************************************************************/
96 : /* GMLReader() */
97 : /************************************************************************/
98 :
99 431 : GMLReader::GMLReader(
100 : #if !defined(HAVE_EXPAT) || !defined(HAVE_XERCES)
101 : CPL_UNUSED
102 : #endif
103 : bool bUseExpatParserPreferably,
104 : bool bInvertAxisOrderIfLatLong, bool bConsiderEPSGAsURN,
105 431 : GMLSwapCoordinatesEnum eSwapCoordinates, bool bGetSecondaryGeometryOption)
106 : : // Experimental. Not publicly advertized. See commented doc in
107 : // drv_gml.html
108 : m_bFetchAllGeometries(
109 431 : CPLTestBool(CPLGetConfigOption("GML_FETCH_ALL_GEOMETRIES", "NO"))),
110 : m_bInvertAxisOrderIfLatLong(bInvertAxisOrderIfLatLong),
111 : m_bConsiderEPSGAsURN(bConsiderEPSGAsURN),
112 : m_eSwapCoordinates(eSwapCoordinates),
113 : m_bGetSecondaryGeometryOption(bGetSecondaryGeometryOption),
114 : // Must be in synced in OGR_G_CreateFromGML(), OGRGMLLayer::OGRGMLLayer(),
115 : // and GMLReader::GMLReader().
116 : m_bFaceHoleNegative(
117 862 : CPLTestBool(CPLGetConfigOption("GML_FACE_HOLE_NEGATIVE", "NO")))
118 : {
119 : #ifndef HAVE_XERCES
120 : #else
121 : #ifdef HAVE_EXPAT
122 431 : if (bUseExpatParserPreferably)
123 429 : bUseExpatReader = true;
124 : #endif
125 : #endif
126 :
127 : #if defined(HAVE_EXPAT) && defined(HAVE_XERCES)
128 431 : if (bUseExpatReader)
129 429 : CPLDebug("GML", "Using Expat reader");
130 : else
131 2 : CPLDebug("GML", "Using Xerces reader");
132 : #endif
133 431 : }
134 :
135 : /************************************************************************/
136 : /* ~GMLReader() */
137 : /************************************************************************/
138 :
139 862 : GMLReader::~GMLReader()
140 :
141 : {
142 431 : GMLReader::ClearClasses();
143 :
144 431 : CPLFree(m_pszFilename);
145 :
146 431 : CleanupParser();
147 :
148 431 : delete m_poRecycledState;
149 :
150 : #ifdef HAVE_XERCES
151 431 : if (m_bXercesInitialized)
152 2 : OGRDeinitializeXerces();
153 : #endif
154 : #ifdef HAVE_EXPAT
155 431 : CPLFree(pabyBuf);
156 : #endif
157 :
158 431 : if (fpGML)
159 430 : VSIFCloseL(fpGML);
160 431 : fpGML = nullptr;
161 :
162 431 : CPLFree(m_pszGlobalSRSName);
163 :
164 431 : CPLFree(m_pszFilteredClassName);
165 862 : }
166 :
167 : /************************************************************************/
168 : /* SetSourceFile() */
169 : /************************************************************************/
170 :
171 434 : void GMLReader::SetSourceFile(const char *pszFilename)
172 :
173 : {
174 434 : CPLFree(m_pszFilename);
175 434 : m_pszFilename = CPLStrdup(pszFilename);
176 434 : }
177 :
178 : /************************************************************************/
179 : /* GetSourceFileName() */
180 : /************************************************************************/
181 :
182 430 : const char *GMLReader::GetSourceFileName()
183 : {
184 430 : return m_pszFilename;
185 : }
186 :
187 : /************************************************************************/
188 : /* SetFP() */
189 : /************************************************************************/
190 :
191 421 : void GMLReader::SetFP(VSILFILE *fp)
192 : {
193 421 : fpGML = fp;
194 421 : }
195 :
196 : /************************************************************************/
197 : /* SetupParser() */
198 : /************************************************************************/
199 :
200 554 : bool GMLReader::SetupParser()
201 :
202 : {
203 554 : if (fpGML == nullptr)
204 12 : fpGML = VSIFOpenL(m_pszFilename, "rt");
205 554 : if (fpGML != nullptr)
206 554 : VSIFSeekL(fpGML, 0, SEEK_SET);
207 :
208 554 : int bRet = -1;
209 : #ifdef HAVE_EXPAT
210 554 : if (bUseExpatReader)
211 552 : bRet = SetupParserExpat();
212 : #endif
213 :
214 : #ifdef HAVE_XERCES
215 554 : if (!bUseExpatReader)
216 2 : bRet = SetupParserXerces();
217 : #endif
218 554 : if (bRet < 0)
219 : {
220 0 : CPLError(CE_Failure, CPLE_AppDefined,
221 : "SetupParser(): should not happen");
222 0 : return false;
223 : }
224 :
225 554 : if (!bRet)
226 0 : return false;
227 :
228 554 : m_bReadStarted = false;
229 :
230 : // Push an empty state.
231 554 : PushState(m_poRecycledState ? m_poRecycledState : new GMLReadState());
232 554 : m_poRecycledState = nullptr;
233 :
234 554 : return true;
235 : }
236 :
237 : #ifdef HAVE_XERCES
238 : /************************************************************************/
239 : /* SetupParserXerces() */
240 : /************************************************************************/
241 :
242 2 : bool GMLReader::SetupParserXerces()
243 : {
244 2 : if (!m_bXercesInitialized)
245 : {
246 2 : if (!OGRInitializeXerces())
247 0 : return false;
248 2 : m_bXercesInitialized = true;
249 : }
250 :
251 : // Cleanup any old parser.
252 2 : if (m_poSAXReader != nullptr)
253 0 : CleanupParser();
254 :
255 : // Create and initialize parser.
256 2 : XMLCh *xmlUriValid = nullptr;
257 2 : XMLCh *xmlUriNS = nullptr;
258 :
259 : try
260 : {
261 2 : m_poSAXReader = XMLReaderFactory::createXMLReader();
262 :
263 2 : GMLXercesHandler *poXercesHandler = new GMLXercesHandler(this);
264 2 : m_poGMLHandler = poXercesHandler;
265 :
266 2 : m_poSAXReader->setContentHandler(poXercesHandler);
267 2 : m_poSAXReader->setErrorHandler(poXercesHandler);
268 2 : m_poSAXReader->setLexicalHandler(poXercesHandler);
269 2 : m_poSAXReader->setEntityResolver(poXercesHandler);
270 2 : m_poSAXReader->setDTDHandler(poXercesHandler);
271 2 : m_poSAXReader->setFeature(
272 2 : XMLUni::fgXercesDisableDefaultEntityResolution, true);
273 :
274 2 : xmlUriValid =
275 2 : XMLString::transcode("http://xml.org/sax/features/validation");
276 2 : xmlUriNS =
277 2 : XMLString::transcode("http://xml.org/sax/features/namespaces");
278 :
279 : #if (OGR_GML_VALIDATION)
280 : m_poSAXReader->setFeature(xmlUriValid, true);
281 : m_poSAXReader->setFeature(xmlUriNS, true);
282 :
283 : m_poSAXReader->setFeature(XMLUni::fgSAX2CoreNameSpaces, true);
284 : m_poSAXReader->setFeature(XMLUni::fgXercesSchema, true);
285 :
286 : // m_poSAXReader->setDoSchema(true);
287 : // m_poSAXReader->setValidationSchemaFullChecking(true);
288 : #else
289 2 : m_poSAXReader->setFeature(XMLUni::fgSAX2CoreValidation, false);
290 :
291 2 : m_poSAXReader->setFeature(XMLUni::fgXercesSchema, false);
292 :
293 : #endif
294 2 : XMLString::release(&xmlUriValid);
295 2 : XMLString::release(&xmlUriNS);
296 : }
297 0 : catch (...)
298 : {
299 0 : XMLString::release(&xmlUriValid);
300 0 : XMLString::release(&xmlUriNS);
301 :
302 0 : CPLError(CE_Warning, CPLE_AppDefined,
303 : "Exception initializing Xerces based GML reader.\n");
304 0 : return false;
305 : }
306 :
307 2 : if (m_GMLInputSource == nullptr && fpGML != nullptr)
308 2 : m_GMLInputSource = OGRCreateXercesInputSource(fpGML);
309 :
310 2 : return true;
311 : }
312 : #endif
313 :
314 : /************************************************************************/
315 : /* SetupParserExpat() */
316 : /************************************************************************/
317 :
318 : #ifdef HAVE_EXPAT
319 552 : bool GMLReader::SetupParserExpat()
320 : {
321 : // Cleanup any old parser.
322 552 : if (oParser != nullptr)
323 3 : CleanupParser();
324 :
325 552 : oParser = OGRCreateExpatXMLParser();
326 552 : m_poGMLHandler = new GMLExpatHandler(this, oParser);
327 :
328 552 : XML_SetElementHandler(oParser, GMLExpatHandler::startElementCbk,
329 : GMLExpatHandler::endElementCbk);
330 552 : XML_SetCharacterDataHandler(oParser, GMLExpatHandler::dataHandlerCbk);
331 552 : XML_SetUserData(oParser, m_poGMLHandler);
332 :
333 552 : if (pabyBuf == nullptr)
334 333 : pabyBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(PARSER_BUF_SIZE));
335 552 : if (pabyBuf == nullptr)
336 0 : return false;
337 :
338 552 : return true;
339 : }
340 : #endif
341 :
342 : /************************************************************************/
343 : /* CleanupParser() */
344 : /************************************************************************/
345 :
346 1283 : void GMLReader::CleanupParser()
347 :
348 : {
349 : #ifdef HAVE_XERCES
350 1283 : if (!bUseExpatReader && m_poSAXReader == nullptr)
351 3 : return;
352 : #endif
353 :
354 : #ifdef HAVE_EXPAT
355 1280 : if (bUseExpatReader && oParser == nullptr)
356 726 : return;
357 : #endif
358 :
359 1112 : while (m_poState)
360 558 : PopState();
361 :
362 : #ifdef HAVE_XERCES
363 554 : delete m_poSAXReader;
364 554 : m_poSAXReader = nullptr;
365 554 : OGRDestroyXercesInputSource(m_GMLInputSource);
366 554 : m_GMLInputSource = nullptr;
367 554 : delete m_poCompleteFeature;
368 554 : m_poCompleteFeature = nullptr;
369 554 : m_bEOF = false;
370 : #endif
371 :
372 : #ifdef HAVE_EXPAT
373 554 : if (oParser)
374 552 : XML_ParserFree(oParser);
375 554 : oParser = nullptr;
376 :
377 1165 : for (int i = nFeatureTabIndex; i < nFeatureTabLength; i++)
378 611 : delete ppoFeatureTab[i];
379 554 : CPLFree(ppoFeatureTab);
380 554 : nFeatureTabIndex = 0;
381 554 : nFeatureTabLength = 0;
382 554 : nFeatureTabAlloc = 0;
383 554 : ppoFeatureTab = nullptr;
384 554 : m_osErrorMessage.clear();
385 :
386 : #endif
387 :
388 554 : delete m_poGMLHandler;
389 554 : m_poGMLHandler = nullptr;
390 :
391 554 : m_bReadStarted = false;
392 : }
393 :
394 : /************************************************************************/
395 : /* NextFeatureXerces() */
396 : /************************************************************************/
397 :
398 : #ifdef HAVE_XERCES
399 2 : GMLFeature *GMLReader::NextFeatureXerces()
400 :
401 : {
402 2 : GMLFeature *poReturn = nullptr;
403 :
404 2 : if (m_bEOF)
405 0 : return nullptr;
406 :
407 : try
408 : {
409 2 : if (!m_bReadStarted)
410 : {
411 2 : if (m_poSAXReader == nullptr)
412 2 : SetupParser();
413 :
414 2 : m_bReadStarted = true;
415 :
416 2 : if (m_poSAXReader == nullptr || m_GMLInputSource == nullptr)
417 0 : return nullptr;
418 :
419 2 : if (!m_poSAXReader->parseFirst(*m_GMLInputSource, m_oToFill))
420 0 : return nullptr;
421 : }
422 :
423 942 : while (m_poCompleteFeature == nullptr && !m_bStopParsing &&
424 940 : m_poSAXReader->parseNext(m_oToFill))
425 : {
426 : }
427 :
428 1 : if (m_poCompleteFeature == nullptr)
429 0 : m_bEOF = true;
430 :
431 1 : poReturn = m_poCompleteFeature;
432 1 : m_poCompleteFeature = nullptr;
433 : }
434 0 : catch (const XMLException &toCatch)
435 : {
436 0 : CPLString osErrMsg;
437 0 : transcode(toCatch.getMessage(), osErrMsg);
438 0 : CPLDebug("GML", "Error during NextFeature()! Message:\n%s",
439 : osErrMsg.c_str());
440 0 : m_bStopParsing = true;
441 : }
442 2 : catch (const SAXException &toCatch)
443 : {
444 1 : CPLString osErrMsg;
445 1 : transcode(toCatch.getMessage(), osErrMsg);
446 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrMsg.c_str());
447 1 : m_bStopParsing = true;
448 : }
449 :
450 2 : return poReturn;
451 : }
452 : #endif
453 :
454 : #ifdef HAVE_EXPAT
455 2255 : GMLFeature *GMLReader::NextFeatureExpat()
456 :
457 : {
458 2255 : if (!m_bReadStarted)
459 : {
460 552 : if (oParser == nullptr)
461 434 : SetupParser();
462 :
463 552 : m_bReadStarted = true;
464 : }
465 :
466 2255 : if (nFeatureTabIndex < nFeatureTabLength)
467 : {
468 1501 : return ppoFeatureTab[nFeatureTabIndex++];
469 : }
470 :
471 754 : if (!m_osErrorMessage.empty())
472 : {
473 2 : CPLError(CE_Failure, CPLE_AppDefined, "%s", m_osErrorMessage.c_str());
474 2 : m_osErrorMessage.clear();
475 2 : return nullptr;
476 : }
477 :
478 1304 : if (fpGML == nullptr || m_bStopParsing || VSIFEofL(fpGML) ||
479 552 : VSIFErrorL(fpGML))
480 200 : return nullptr;
481 :
482 552 : nFeatureTabLength = 0;
483 552 : nFeatureTabIndex = 0;
484 :
485 552 : int nDone = 0;
486 0 : do
487 : {
488 : // Reset counter that is used to detect billion laugh attacks.
489 552 : static_cast<GMLExpatHandler *>(m_poGMLHandler)
490 552 : ->ResetDataHandlerCounter();
491 :
492 : unsigned int nLen = static_cast<unsigned int>(
493 552 : VSIFReadL(pabyBuf, 1, PARSER_BUF_SIZE, fpGML));
494 552 : nDone = nLen < PARSER_BUF_SIZE;
495 :
496 : // Some files, such as APT_AIXM.xml from
497 : // https://nfdc.faa.gov/webContent/56DaySub/2015-03-05/aixm5.1.zip
498 : // end with trailing nul characters. This test is not fully bullet-proof
499 : // in case the nul characters would occur at a buffer boundary.
500 552 : while (nDone && nLen > 0 && pabyBuf[nLen - 1] == '\0')
501 0 : nLen--;
502 :
503 552 : if (XML_Parse(oParser, pabyBuf, nLen, nDone) == XML_STATUS_ERROR)
504 : {
505 : // Defer emission of the error message until we have to return
506 : // nullptr
507 : m_osErrorMessage.Printf(
508 : "XML parsing of GML file failed : %s "
509 : "at line %d, column %d",
510 : XML_ErrorString(XML_GetErrorCode(oParser)),
511 10 : static_cast<int>(XML_GetCurrentLineNumber(oParser)),
512 5 : static_cast<int>(XML_GetCurrentColumnNumber(oParser)));
513 5 : m_bStopParsing = true;
514 : }
515 552 : if (!m_bStopParsing)
516 547 : m_bStopParsing = static_cast<GMLExpatHandler *>(m_poGMLHandler)
517 547 : ->HasStoppedParsing();
518 552 : } while (!nDone && !m_bStopParsing && nFeatureTabLength == 0);
519 :
520 552 : if (nFeatureTabLength)
521 508 : return ppoFeatureTab[nFeatureTabIndex++];
522 :
523 44 : if (!m_osErrorMessage.empty())
524 : {
525 3 : CPLError(CE_Failure, CPLE_AppDefined, "%s", m_osErrorMessage.c_str());
526 3 : m_osErrorMessage.clear();
527 : }
528 :
529 44 : return nullptr;
530 : }
531 : #endif
532 :
533 2257 : GMLFeature *GMLReader::NextFeature()
534 : {
535 : #ifdef HAVE_EXPAT
536 2257 : if (bUseExpatReader)
537 2255 : return NextFeatureExpat();
538 : #ifdef HAVE_XERCES
539 2 : return NextFeatureXerces();
540 : #else
541 : CPLError(CE_Failure, CPLE_AppDefined, "NextFeature(): Should not happen");
542 : return nullptr;
543 : #endif
544 : #else
545 : #ifdef HAVE_XERCES
546 : if (!bUseExpatReader)
547 : return NextFeatureXerces();
548 : #endif
549 : CPLError(CE_Failure, CPLE_AppDefined, "NextFeature(): Should not happen");
550 : return nullptr;
551 : #endif
552 : }
553 :
554 : /************************************************************************/
555 : /* PushFeature() */
556 : /* */
557 : /* Create a feature based on the named element. If the */
558 : /* corresponding feature class doesn't exist yet, then create */
559 : /* it now. A new GMLReadState will be created for the feature, */
560 : /* and it will be placed within that state. The state is */
561 : /* pushed onto the readstate stack. */
562 : /************************************************************************/
563 :
564 2621 : void GMLReader::PushFeature(const char *pszElement, const char *pszFID,
565 : int nClassIndex)
566 :
567 : {
568 2621 : int iClass = 0;
569 :
570 2621 : if (nClassIndex != INT_MAX)
571 : {
572 1203 : iClass = nClassIndex;
573 : }
574 : else
575 : {
576 : /* --------------------------------------------------------------------
577 : */
578 : /* Find the class of this element. */
579 : /* --------------------------------------------------------------------
580 : */
581 3650 : for (; iClass < m_nClassCount; iClass++)
582 : {
583 3523 : if (EQUAL(pszElement, m_papoClass[iClass]->GetElementName()))
584 1291 : break;
585 : }
586 :
587 : /* --------------------------------------------------------------------
588 : */
589 : /* Create a new feature class for this element, if there is no */
590 : /* existing class for it. */
591 : /* --------------------------------------------------------------------
592 : */
593 1418 : if (iClass == m_nClassCount)
594 : {
595 127 : CPLAssert(!m_bClassListLocked);
596 :
597 127 : GMLFeatureClass *poNewClass = new GMLFeatureClass(pszElement);
598 :
599 127 : AddClass(poNewClass);
600 : }
601 : }
602 :
603 : /* -------------------------------------------------------------------- */
604 : /* Create a feature of this feature class. Try to set the fid */
605 : /* if available. */
606 : /* -------------------------------------------------------------------- */
607 2621 : GMLFeature *poFeature = new GMLFeature(m_papoClass[iClass]);
608 2621 : if (pszFID != nullptr)
609 : {
610 1597 : poFeature->SetFID(pszFID);
611 : }
612 :
613 : /* -------------------------------------------------------------------- */
614 : /* Create and push a new read state. */
615 : /* -------------------------------------------------------------------- */
616 : GMLReadState *poState =
617 2621 : m_poRecycledState ? m_poRecycledState : new GMLReadState();
618 2621 : m_poRecycledState = nullptr;
619 2621 : poState->m_poFeature = poFeature;
620 2621 : PushState(poState);
621 2621 : }
622 :
623 : /************************************************************************/
624 : /* IsFeatureElement() */
625 : /* */
626 : /* Based on context and the element name, is this element a new */
627 : /* GML feature element? */
628 : /************************************************************************/
629 :
630 5466 : int GMLReader::GetFeatureElementIndex(const char *pszElement,
631 : int nElementLength,
632 : GMLAppSchemaType eAppSchemaType)
633 :
634 : {
635 5466 : const char *pszLast = m_poState->GetLastComponent();
636 5466 : const size_t nLenLast = m_poState->GetLastComponentLen();
637 :
638 5466 : if (eAppSchemaType == APPSCHEMA_MTKGML)
639 : {
640 44 : if (m_poState->m_nPathLength != 1)
641 22 : return -1;
642 : }
643 5422 : else if ((nLenLast >= 6 && EQUAL(pszLast + nLenLast - 6, "member")) ||
644 1447 : (nLenLast >= 7 && EQUAL(pszLast + nLenLast - 7, "members")))
645 : {
646 : // Default feature name.
647 : }
648 : else
649 : {
650 2665 : if (nLenLast == 4 && strcmp(pszLast, "dane") == 0)
651 : {
652 : // Polish TBD GML.
653 : }
654 :
655 : // Begin of OpenLS.
656 2665 : else if (nLenLast == 19 && nElementLength == 15 &&
657 1 : strcmp(pszLast, "GeocodeResponseList") == 0 &&
658 0 : strcmp(pszElement, "GeocodedAddress") == 0)
659 : {
660 : }
661 2665 : else if (nLenLast == 22 &&
662 3 : strcmp(pszLast, "DetermineRouteResponse") == 0)
663 : {
664 : // We don't want the children of RouteInstructionsList
665 : // to be a single feature. We want each RouteInstruction
666 : // to be a feature.
667 3 : if (strcmp(pszElement, "RouteInstructionsList") == 0)
668 1 : return -1;
669 : }
670 2662 : else if (nElementLength == 16 && nLenLast == 21 &&
671 9 : strcmp(pszElement, "RouteInstruction") == 0 &&
672 9 : strcmp(pszLast, "RouteInstructionsList") == 0)
673 : {
674 : }
675 : // End of OpenLS.
676 :
677 2653 : else if (nLenLast > 6 &&
678 513 : strcmp(pszLast + nLenLast - 6, "_layer") == 0 &&
679 0 : nElementLength > 8 &&
680 0 : strcmp(pszElement + nElementLength - 8, "_feature") == 0)
681 : {
682 : // GML answer of MapServer WMS GetFeatureInfo request.
683 : }
684 :
685 : // Begin of CSW SearchResults.
686 2653 : else if (nElementLength == static_cast<int>(strlen("BriefRecord")) &&
687 4 : nLenLast == strlen("SearchResults") &&
688 4 : strcmp(pszElement, "BriefRecord") == 0 &&
689 4 : strcmp(pszLast, "SearchResults") == 0)
690 : {
691 : }
692 2649 : else if (nElementLength == static_cast<int>(strlen("SummaryRecord")) &&
693 4 : nLenLast == strlen("SearchResults") &&
694 4 : strcmp(pszElement, "SummaryRecord") == 0 &&
695 4 : strcmp(pszLast, "SearchResults") == 0)
696 : {
697 : }
698 2645 : else if (nElementLength == static_cast<int>(strlen("Record")) &&
699 45 : nLenLast == strlen("SearchResults") &&
700 45 : strcmp(pszElement, "Record") == 0 &&
701 44 : strcmp(pszLast, "SearchResults") == 0)
702 : {
703 : }
704 : /* End of CSW SearchResults */
705 :
706 : else
707 : {
708 2601 : if (m_bClassListLocked)
709 : {
710 12385 : for (int i = 0; i < m_nClassCount; i++)
711 : {
712 10842 : if (m_poState->osPath.size() + 1 + nElementLength ==
713 10842 : m_papoClass[i]->GetElementNameLen() &&
714 158 : m_papoClass[i]
715 158 : ->GetElementName()[m_poState->osPath.size()] ==
716 103 : '|' &&
717 103 : memcmp(m_poState->osPath.c_str(),
718 103 : m_papoClass[i]->GetElementName(),
719 11103 : m_poState->osPath.size()) == 0 &&
720 206 : memcmp(pszElement,
721 206 : m_papoClass[i]->GetElementName() + 1 +
722 103 : m_poState->osPath.size(),
723 : nElementLength) == 0)
724 : {
725 81 : return i;
726 : }
727 : }
728 : // Give a chance to find a feature class by element name
729 : // This is for example needed for
730 : // autotest/ogr/data/gml_jpfgd/BldA.xml that has a
731 : // feature at a low nesting level.
732 : }
733 : else
734 : {
735 977 : return -1;
736 : }
737 : }
738 : }
739 :
740 : // If the class list isn't locked, any element that is a featureMember
741 : // will do.
742 4385 : if (!m_bClassListLocked)
743 1418 : return INT_MAX;
744 :
745 : // otherwise, find a class with the desired element name.
746 17128 : for (int i = 0; i < m_nClassCount; i++)
747 : {
748 31158 : if (nElementLength ==
749 17899 : static_cast<int>(m_papoClass[i]->GetElementNameLen()) &&
750 2320 : memcmp(pszElement, m_papoClass[i]->GetElementName(),
751 : nElementLength) == 0)
752 1418 : return i;
753 : }
754 :
755 1549 : return -1;
756 : }
757 :
758 : /************************************************************************/
759 : /* IsCityGMLGenericAttributeElement() */
760 : /************************************************************************/
761 :
762 132 : bool GMLReader::IsCityGMLGenericAttributeElement(const char *pszElement,
763 : void *attr)
764 :
765 : {
766 132 : if (strcmp(pszElement, "stringAttribute") != 0 &&
767 94 : strcmp(pszElement, "intAttribute") != 0 &&
768 92 : strcmp(pszElement, "doubleAttribute") != 0)
769 90 : return false;
770 :
771 42 : char *pszVal = m_poGMLHandler->GetAttributeValue(attr, "name");
772 42 : if (pszVal == nullptr)
773 0 : return false;
774 :
775 42 : GMLFeatureClass *poClass = m_poState->m_poFeature->GetClass();
776 :
777 : // If the schema is not yet locked, then any simple element
778 : // is potentially an attribute.
779 42 : if (!poClass->IsSchemaLocked())
780 : {
781 42 : CPLFree(pszVal);
782 42 : return true;
783 : }
784 :
785 0 : for (int i = 0; i < poClass->GetPropertyCount(); i++)
786 : {
787 0 : if (strcmp(poClass->GetProperty(i)->GetSrcElement(), pszVal) == 0)
788 : {
789 0 : CPLFree(pszVal);
790 0 : return true;
791 : }
792 : }
793 :
794 0 : CPLFree(pszVal);
795 0 : return false;
796 : }
797 :
798 : /************************************************************************/
799 : /* GetAttributeElementIndex() */
800 : /************************************************************************/
801 :
802 11848 : int GMLReader::GetAttributeElementIndex(const char *pszElement, int nLen,
803 : const char *pszAttrKey)
804 :
805 : {
806 11848 : GMLFeatureClass *poClass = m_poState->m_poFeature->GetClass();
807 :
808 : // If the schema is not yet locked, then any simple element
809 : // is potentially an attribute.
810 11848 : if (!poClass->IsSchemaLocked())
811 6372 : return INT_MAX;
812 :
813 : // Otherwise build the path to this element into a single string
814 : // and compare against known attributes.
815 5476 : if (m_poState->m_nPathLength == 0)
816 : {
817 3618 : if (pszAttrKey == nullptr)
818 3545 : return poClass->GetPropertyIndexBySrcElement(pszElement, nLen);
819 : else
820 : {
821 73 : int nFullLen = nLen + 1 + static_cast<int>(strlen(pszAttrKey));
822 73 : osElemPath.reserve(nFullLen);
823 73 : osElemPath.assign(pszElement, nLen);
824 73 : osElemPath.append(1, '@');
825 73 : osElemPath.append(pszAttrKey);
826 73 : return poClass->GetPropertyIndexBySrcElement(osElemPath.c_str(),
827 73 : nFullLen);
828 : }
829 : }
830 : else
831 : {
832 1858 : int nFullLen = nLen + static_cast<int>(m_poState->osPath.size()) + 1;
833 1858 : if (pszAttrKey != nullptr)
834 89 : nFullLen += 1 + static_cast<int>(strlen(pszAttrKey));
835 1858 : osElemPath.reserve(nFullLen);
836 1858 : osElemPath.assign(m_poState->osPath);
837 1858 : osElemPath.append(1, '|');
838 1858 : osElemPath.append(pszElement, nLen);
839 1858 : if (pszAttrKey != nullptr)
840 : {
841 89 : osElemPath.append(1, '@');
842 89 : osElemPath.append(pszAttrKey);
843 : }
844 1858 : return poClass->GetPropertyIndexBySrcElement(osElemPath.c_str(),
845 1858 : nFullLen);
846 : }
847 : }
848 :
849 : /************************************************************************/
850 : /* PopState() */
851 : /************************************************************************/
852 :
853 3175 : void GMLReader::PopState()
854 :
855 : {
856 3175 : if (m_poState != nullptr)
857 : {
858 : #ifdef HAVE_XERCES
859 3175 : if (!bUseExpatReader && m_poState->m_poFeature != nullptr &&
860 1 : m_poCompleteFeature == nullptr)
861 : {
862 1 : m_poCompleteFeature = m_poState->m_poFeature;
863 1 : m_poState->m_poFeature = nullptr;
864 : }
865 3174 : else if (!bUseExpatReader && m_poState->m_poFeature != nullptr)
866 : {
867 0 : delete m_poState->m_poFeature;
868 0 : m_poState->m_poFeature = nullptr;
869 : }
870 : #endif
871 :
872 : #ifdef HAVE_EXPAT
873 3175 : if (bUseExpatReader && m_poState->m_poFeature != nullptr)
874 : {
875 2620 : if (nFeatureTabLength >= nFeatureTabAlloc)
876 : {
877 563 : nFeatureTabAlloc = nFeatureTabLength * 4 / 3 + 16;
878 563 : ppoFeatureTab = static_cast<GMLFeature **>(CPLRealloc(
879 563 : ppoFeatureTab, sizeof(GMLFeature *) * (nFeatureTabAlloc)));
880 : }
881 2620 : ppoFeatureTab[nFeatureTabLength] = m_poState->m_poFeature;
882 2620 : nFeatureTabLength++;
883 :
884 2620 : m_poState->m_poFeature = nullptr;
885 : }
886 : #endif
887 :
888 3175 : GMLReadState *poParent = m_poState->m_poParentState;
889 :
890 3175 : delete m_poRecycledState;
891 3175 : m_poRecycledState = m_poState;
892 3175 : m_poRecycledState->Reset();
893 3175 : m_poState = poParent;
894 : }
895 3175 : }
896 :
897 : /************************************************************************/
898 : /* PushState() */
899 : /************************************************************************/
900 :
901 3175 : void GMLReader::PushState(GMLReadState *poState)
902 :
903 : {
904 3175 : poState->m_poParentState = m_poState;
905 3175 : m_poState = poState;
906 3175 : }
907 :
908 : /************************************************************************/
909 : /* GetClass() */
910 : /************************************************************************/
911 :
912 868 : GMLFeatureClass *GMLReader::GetClass(int iClass) const
913 :
914 : {
915 868 : if (iClass < 0 || iClass >= m_nClassCount)
916 0 : return nullptr;
917 :
918 868 : return m_papoClass[iClass];
919 : }
920 :
921 : /************************************************************************/
922 : /* GetClass() */
923 : /************************************************************************/
924 :
925 1647 : GMLFeatureClass *GMLReader::GetClass(const char *pszName) const
926 :
927 : {
928 2786 : for (int iClass = 0; iClass < m_nClassCount; iClass++)
929 : {
930 1720 : if (EQUAL(m_papoClass[iClass]->GetName(), pszName))
931 581 : return m_papoClass[iClass];
932 : }
933 :
934 1066 : return nullptr;
935 : }
936 :
937 : /************************************************************************/
938 : /* AddClass() */
939 : /************************************************************************/
940 :
941 670 : int GMLReader::AddClass(GMLFeatureClass *poNewClass)
942 :
943 : {
944 670 : CPLAssert(GetClass(poNewClass->GetName()) == nullptr);
945 :
946 670 : m_nClassCount++;
947 670 : m_papoClass = static_cast<GMLFeatureClass **>(
948 670 : CPLRealloc(m_papoClass, sizeof(void *) * m_nClassCount));
949 670 : m_papoClass[m_nClassCount - 1] = poNewClass;
950 :
951 670 : if (poNewClass->HasFeatureProperties())
952 8 : m_bLookForClassAtAnyLevel = true;
953 :
954 670 : return m_nClassCount - 1;
955 : }
956 :
957 : /************************************************************************/
958 : /* ClearClasses() */
959 : /************************************************************************/
960 :
961 581 : void GMLReader::ClearClasses()
962 :
963 : {
964 1246 : for (int i = 0; i < m_nClassCount; i++)
965 665 : delete m_papoClass[i];
966 581 : CPLFree(m_papoClass);
967 :
968 581 : m_nClassCount = 0;
969 581 : m_papoClass = nullptr;
970 581 : m_bLookForClassAtAnyLevel = false;
971 581 : }
972 :
973 : /************************************************************************/
974 : /* SetFeaturePropertyDirectly() */
975 : /* */
976 : /* Set the property value on the current feature, adding the */
977 : /* property name to the GMLFeatureClass if required. */
978 : /* The pszValue ownership is passed to this function. */
979 : /************************************************************************/
980 :
981 7027 : void GMLReader::SetFeaturePropertyDirectly(const char *pszElement,
982 : char *pszValue, int iPropertyIn,
983 : GMLPropertyType eType)
984 :
985 : {
986 7027 : GMLFeature *poFeature = GetState()->m_poFeature;
987 :
988 7027 : CPLAssert(poFeature != nullptr);
989 :
990 : /* -------------------------------------------------------------------- */
991 : /* Does this property exist in the feature class? If not, add */
992 : /* it. */
993 : /* -------------------------------------------------------------------- */
994 7027 : GMLFeatureClass *poClass = poFeature->GetClass();
995 7027 : int iProperty = 0;
996 :
997 7027 : const int nPropertyCount = poClass->GetPropertyCount();
998 7027 : if (iPropertyIn >= 0 && iPropertyIn < nPropertyCount)
999 : {
1000 2920 : iProperty = iPropertyIn;
1001 : }
1002 : else
1003 : {
1004 11844 : for (; iProperty < nPropertyCount; iProperty++)
1005 : {
1006 11429 : if (strcmp(poClass->GetProperty(iProperty)->GetSrcElement(),
1007 11429 : pszElement) == 0)
1008 3692 : break;
1009 : }
1010 :
1011 4107 : if (iProperty == nPropertyCount)
1012 : {
1013 415 : if (poClass->IsSchemaLocked())
1014 : {
1015 1 : CPLDebug("GML",
1016 : "Encountered property missing from class schema : %s.",
1017 : pszElement);
1018 1 : CPLFree(pszValue);
1019 1 : return;
1020 : }
1021 :
1022 414 : CPLString osFieldName;
1023 :
1024 414 : if (IsWFSJointLayer())
1025 : {
1026 : // At that point the element path should be
1027 : // member|layer|property.
1028 :
1029 : // Strip member| prefix. Should always be true normally.
1030 12 : if (STARTS_WITH(pszElement, "member|"))
1031 12 : osFieldName = pszElement + strlen("member|");
1032 :
1033 : // Replace layer|property by layer_property.
1034 12 : size_t iPos = osFieldName.find('|');
1035 12 : if (iPos != std::string::npos)
1036 8 : osFieldName[iPos] = '.';
1037 :
1038 : // Special case for gml:id on layer.
1039 12 : iPos = osFieldName.find("@id");
1040 12 : if (iPos != std::string::npos)
1041 : {
1042 4 : osFieldName.resize(iPos);
1043 4 : osFieldName += ".gml_id";
1044 : }
1045 : }
1046 402 : else if (strchr(pszElement, '|') == nullptr)
1047 : {
1048 308 : osFieldName = pszElement;
1049 : }
1050 : else
1051 : {
1052 94 : osFieldName = strrchr(pszElement, '|') + 1;
1053 94 : if (poClass->GetPropertyIndex(osFieldName) != -1)
1054 9 : osFieldName = pszElement;
1055 : }
1056 :
1057 414 : size_t nPos = osFieldName.find("@");
1058 414 : if (nPos != std::string::npos)
1059 3 : osFieldName[nPos] = '_';
1060 :
1061 : // Does this conflict with an existing property name?
1062 415 : for (int i = 0; poClass->GetProperty(osFieldName) != nullptr; i++)
1063 : {
1064 1 : osFieldName += "_";
1065 1 : if (i == 10)
1066 : {
1067 0 : CPLDebug("GML", "Too many conflicting property names : %s.",
1068 : osFieldName.c_str());
1069 0 : CPLFree(pszValue);
1070 0 : return;
1071 : }
1072 : }
1073 :
1074 : GMLPropertyDefn *poPDefn =
1075 414 : new GMLPropertyDefn(osFieldName, pszElement);
1076 :
1077 414 : if (EQUAL(CPLGetConfigOption("GML_FIELDTYPES", ""),
1078 : "ALWAYS_STRING"))
1079 0 : poPDefn->SetType(GMLPT_String);
1080 414 : else if (eType != GMLPT_Untyped)
1081 3 : poPDefn->SetType(eType);
1082 :
1083 414 : if (poClass->AddProperty(poPDefn) < 0)
1084 : {
1085 0 : delete poPDefn;
1086 0 : CPLFree(pszValue);
1087 0 : return;
1088 : }
1089 : }
1090 : }
1091 :
1092 : /* -------------------------------------------------------------------- */
1093 : /* Set the property */
1094 : /* -------------------------------------------------------------------- */
1095 7026 : poFeature->SetPropertyDirectly(iProperty, pszValue);
1096 :
1097 : /* -------------------------------------------------------------------- */
1098 : /* Do we need to update the property type? */
1099 : /* -------------------------------------------------------------------- */
1100 7026 : if (!poClass->IsSchemaLocked() && !EQUAL(pszValue, OGR_GML_NULL))
1101 : {
1102 3964 : auto poClassProperty = poClass->GetProperty(iProperty);
1103 3964 : if (poClassProperty)
1104 : {
1105 3964 : poClassProperty->AnalysePropertyValue(
1106 3964 : poFeature->GetProperty(iProperty), m_bSetWidthFlag);
1107 : }
1108 : else
1109 : {
1110 0 : CPLAssert(false);
1111 : }
1112 : }
1113 : }
1114 :
1115 : /************************************************************************/
1116 : /* LoadClasses() */
1117 : /************************************************************************/
1118 :
1119 56 : bool GMLReader::LoadClasses(const char *pszFile)
1120 :
1121 : {
1122 : // Add logic later to determine reasonable default schema file.
1123 56 : if (pszFile == nullptr)
1124 0 : return false;
1125 :
1126 : /* -------------------------------------------------------------------- */
1127 : /* Load the raw XML file. */
1128 : /* -------------------------------------------------------------------- */
1129 56 : GByte *pabyRet = nullptr;
1130 56 : const char *pszWholeText = nullptr;
1131 : {
1132 : #ifdef EMBED_RESOURCE_FILES
1133 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1134 : #endif
1135 56 : if (VSIIngestFile(nullptr, pszFile, &pabyRet, nullptr,
1136 56 : 100 * 1024 * 1024))
1137 : {
1138 56 : pszWholeText = reinterpret_cast<const char *>(pabyRet);
1139 : }
1140 : else
1141 : {
1142 : #ifdef EMBED_RESOURCE_FILES
1143 : pszWholeText = GMLGetFileContent(pszFile);
1144 : if (pszWholeText)
1145 : {
1146 : CPLDebug("GML", "Using embedded %s", pszFile);
1147 : }
1148 : #endif
1149 : }
1150 : }
1151 56 : if (!pszWholeText)
1152 0 : return false;
1153 :
1154 56 : if (strstr(pszWholeText, "<GMLFeatureClassList") == nullptr)
1155 : {
1156 0 : VSIFree(pabyRet);
1157 0 : CPLError(CE_Failure, CPLE_AppDefined,
1158 : "File %s does not contain a GMLFeatureClassList tree.",
1159 : pszFile);
1160 0 : return false;
1161 : }
1162 :
1163 : /* -------------------------------------------------------------------- */
1164 : /* Convert to XML parse tree. */
1165 : /* -------------------------------------------------------------------- */
1166 112 : CPLXMLTreeCloser psRoot(CPLParseXMLString(pszWholeText));
1167 56 : VSIFree(pabyRet);
1168 :
1169 : // We assume parser will report errors via CPL.
1170 56 : if (psRoot.get() == nullptr)
1171 0 : return false;
1172 :
1173 110 : if (psRoot->eType != CXT_Element ||
1174 54 : !EQUAL(psRoot->pszValue, "GMLFeatureClassList"))
1175 : {
1176 2 : CPLError(CE_Failure, CPLE_AppDefined,
1177 : "File %s is not a GMLFeatureClassList document.", pszFile);
1178 2 : return false;
1179 : }
1180 :
1181 : const char *pszSequentialLayers =
1182 54 : CPLGetXMLValue(psRoot.get(), "SequentialLayers", nullptr);
1183 54 : if (pszSequentialLayers)
1184 8 : m_nHasSequentialLayers = CPLTestBool(pszSequentialLayers);
1185 :
1186 : /* -------------------------------------------------------------------- */
1187 : /* Extract feature classes for all definitions found. */
1188 : /* -------------------------------------------------------------------- */
1189 192 : for (CPLXMLNode *psThis = psRoot->psChild; psThis != nullptr;
1190 138 : psThis = psThis->psNext)
1191 : {
1192 138 : if (psThis->eType == CXT_Element &&
1193 111 : EQUAL(psThis->pszValue, "GMLFeatureClass"))
1194 : {
1195 103 : GMLFeatureClass *poClass = new GMLFeatureClass();
1196 :
1197 103 : if (!poClass->InitializeFromXML(psThis))
1198 : {
1199 0 : delete poClass;
1200 0 : return false;
1201 : }
1202 :
1203 103 : poClass->SetSchemaLocked(true);
1204 :
1205 103 : AddClass(poClass);
1206 : }
1207 : }
1208 :
1209 54 : SetClassListLocked(true);
1210 :
1211 54 : return true;
1212 : }
1213 :
1214 : /************************************************************************/
1215 : /* SaveClasses() */
1216 : /************************************************************************/
1217 :
1218 94 : bool GMLReader::SaveClasses(const char *pszFile)
1219 :
1220 : {
1221 : // Add logic later to determine reasonable default schema file.
1222 94 : if (pszFile == nullptr)
1223 0 : return false;
1224 :
1225 : /* -------------------------------------------------------------------- */
1226 : /* Create in memory schema tree. */
1227 : /* -------------------------------------------------------------------- */
1228 : CPLXMLNode *psRoot =
1229 94 : CPLCreateXMLNode(nullptr, CXT_Element, "GMLFeatureClassList");
1230 :
1231 94 : if (m_nHasSequentialLayers != -1 && m_nClassCount > 1)
1232 : {
1233 14 : CPLCreateXMLElementAndValue(psRoot, "SequentialLayers",
1234 14 : m_nHasSequentialLayers ? "true" : "false");
1235 : }
1236 :
1237 207 : for (int iClass = 0; iClass < m_nClassCount; iClass++)
1238 : {
1239 113 : CPLAddXMLChild(psRoot, m_papoClass[iClass]->SerializeToXML());
1240 : }
1241 :
1242 : /* -------------------------------------------------------------------- */
1243 : /* Serialize to disk. */
1244 : /* -------------------------------------------------------------------- */
1245 94 : char *pszWholeText = CPLSerializeXMLTree(psRoot);
1246 :
1247 94 : CPLDestroyXMLNode(psRoot);
1248 :
1249 94 : auto fp = VSIVirtualHandleUniquePtr(VSIFOpenL(pszFile, "wb"));
1250 :
1251 94 : bool bSuccess = true;
1252 94 : if (fp == nullptr)
1253 0 : bSuccess = false;
1254 : else
1255 : {
1256 94 : if (fp->Write(pszWholeText, strlen(pszWholeText), 1) != 1)
1257 0 : bSuccess = false;
1258 : }
1259 :
1260 94 : CPLFree(pszWholeText);
1261 :
1262 94 : return bSuccess;
1263 : }
1264 :
1265 : /************************************************************************/
1266 : /* PrescanForSchema() */
1267 : /* */
1268 : /* For now we use a pretty dumb approach of just doing a normal */
1269 : /* scan of the whole file, building up the schema information. */
1270 : /* Eventually we hope to do a more efficient scan when just */
1271 : /* looking for schema information. */
1272 : /************************************************************************/
1273 :
1274 112 : bool GMLReader::PrescanForSchema(bool bGetExtents, bool bOnlyDetectSRS)
1275 :
1276 : {
1277 112 : if (m_pszFilename == nullptr)
1278 0 : return false;
1279 :
1280 112 : if (!bOnlyDetectSRS)
1281 : {
1282 103 : SetClassListLocked(false);
1283 103 : ClearClasses();
1284 : }
1285 :
1286 112 : if (!SetupParser())
1287 0 : return false;
1288 :
1289 112 : m_bCanUseGlobalSRSName = true;
1290 :
1291 112 : GMLFeatureClass *poLastClass = nullptr;
1292 :
1293 112 : m_nHasSequentialLayers = TRUE;
1294 :
1295 112 : void *hCacheSRS = GML_BuildOGRGeometryFromList_CreateCache();
1296 :
1297 224 : std::string osWork;
1298 :
1299 121 : for (int i = 0; i < m_nClassCount; i++)
1300 : {
1301 9 : m_papoClass[i]->SetFeatureCount(-1);
1302 9 : m_papoClass[i]->SetSRSName(nullptr);
1303 : }
1304 :
1305 112 : GMLFeature *poFeature = nullptr;
1306 112 : std::set<GMLFeatureClass *> knownClasses;
1307 112 : bool bFoundPerFeatureSRSName = false;
1308 :
1309 642 : while ((poFeature = NextFeature()) != nullptr)
1310 : {
1311 530 : GMLFeatureClass *poClass = poFeature->GetClass();
1312 :
1313 530 : if (knownClasses.find(poClass) == knownClasses.end())
1314 : {
1315 126 : knownClasses.insert(poClass);
1316 126 : if (m_pszGlobalSRSName && GML_IsLegitSRSName(m_pszGlobalSRSName))
1317 : {
1318 37 : poClass->SetSRSName(m_pszGlobalSRSName);
1319 : }
1320 : }
1321 :
1322 560 : if (poLastClass != nullptr && poClass != poLastClass &&
1323 30 : poClass->GetFeatureCount() != -1)
1324 0 : m_nHasSequentialLayers = false;
1325 530 : poLastClass = poClass;
1326 :
1327 530 : if (poClass->GetFeatureCount() == -1)
1328 126 : poClass->SetFeatureCount(1);
1329 : else
1330 404 : poClass->SetFeatureCount(poClass->GetFeatureCount() + 1);
1331 :
1332 530 : const CPLXMLNode *const *papsGeometry = poFeature->GetGeometryList();
1333 530 : bool bGeometryColumnJustCreated = false;
1334 :
1335 530 : const CPLXMLNode *apsGeometries[2] = {nullptr, nullptr};
1336 : const CPLXMLNode *psBoundedByGeometry =
1337 530 : poFeature->GetBoundedByGeometry();
1338 530 : int nFeatureGeomCount = poFeature->GetGeometryCount();
1339 530 : if (psBoundedByGeometry && nFeatureGeomCount == 0 &&
1340 3 : strcmp(psBoundedByGeometry->pszValue, "null") != 0)
1341 : {
1342 1 : apsGeometries[0] = psBoundedByGeometry;
1343 1 : papsGeometry = apsGeometries;
1344 1 : nFeatureGeomCount = 1;
1345 : }
1346 :
1347 530 : if (!bOnlyDetectSRS && papsGeometry != nullptr &&
1348 511 : papsGeometry[0] != nullptr)
1349 : {
1350 472 : if (poClass->GetGeometryPropertyCount() == 0)
1351 : {
1352 196 : std::string osPath(poClass->GetSingleGeomElemPath());
1353 123 : if (osPath.empty() &&
1354 123 : poClass->IsConsistentSingleGeomElemPath() &&
1355 21 : papsGeometry[0] == psBoundedByGeometry)
1356 : {
1357 1 : osPath = "boundedBy";
1358 : }
1359 98 : std::string osGeomName(osPath);
1360 98 : const auto nPos = osGeomName.rfind('|');
1361 98 : if (nPos != std::string::npos)
1362 1 : osGeomName = osGeomName.substr(nPos + 1);
1363 98 : bGeometryColumnJustCreated = true;
1364 196 : poClass->AddGeometryProperty(new GMLGeometryPropertyDefn(
1365 196 : osGeomName.c_str(), osPath.c_str(), wkbUnknown, -1, true));
1366 : }
1367 : }
1368 :
1369 530 : if (bGetExtents && papsGeometry != nullptr)
1370 : {
1371 : const int nIters = std::min(nFeatureGeomCount,
1372 530 : poClass->GetGeometryPropertyCount());
1373 1029 : for (int i = 0; i < nIters; ++i)
1374 : {
1375 499 : if (papsGeometry[i] == nullptr)
1376 7 : continue;
1377 :
1378 494 : const CPLXMLNode *myGeometryList[2] = {papsGeometry[i],
1379 494 : nullptr};
1380 988 : OGRGeometry *poGeometry = GML_BuildOGRGeometryFromList(
1381 494 : myGeometryList, true, m_bInvertAxisOrderIfLatLong, nullptr,
1382 494 : m_bConsiderEPSGAsURN, m_eSwapCoordinates,
1383 494 : m_bGetSecondaryGeometryOption, hCacheSRS,
1384 494 : m_bFaceHoleNegative);
1385 494 : if (poGeometry == nullptr)
1386 2 : continue;
1387 :
1388 492 : auto poGeomProperty = poClass->GetGeometryProperty(i);
1389 : OGRwkbGeometryType eGType =
1390 492 : static_cast<OGRwkbGeometryType>(poGeomProperty->GetType());
1391 :
1392 984 : const char *pszSRSName = GML_ExtractSrsNameFromGeometry(
1393 492 : myGeometryList, osWork, m_bConsiderEPSGAsURN);
1394 492 : if (pszSRSName != nullptr)
1395 142 : bFoundPerFeatureSRSName = true;
1396 :
1397 492 : if (pszSRSName != nullptr && m_pszGlobalSRSName != nullptr &&
1398 73 : !EQUAL(pszSRSName, m_pszGlobalSRSName))
1399 : {
1400 0 : m_bCanUseGlobalSRSName = false;
1401 : }
1402 492 : if (m_pszGlobalSRSName == nullptr || pszSRSName != nullptr)
1403 : {
1404 236 : if (poClass->GetGeometryPropertyCount() == 1)
1405 227 : poClass->MergeSRSName(pszSRSName);
1406 : else
1407 9 : poGeomProperty->MergeSRSName(pszSRSName ? pszSRSName
1408 : : "");
1409 : }
1410 :
1411 : // Merge geometry type into layer.
1412 492 : if (bGeometryColumnJustCreated)
1413 : {
1414 96 : poGeomProperty->SetType(poGeometry->getGeometryType());
1415 : }
1416 : else
1417 : {
1418 396 : poGeomProperty->SetType(OGRMergeGeometryTypesEx(
1419 396 : eGType, poGeometry->getGeometryType(), true));
1420 : }
1421 :
1422 : // Merge extents.
1423 492 : if (!poGeometry->IsEmpty())
1424 : {
1425 491 : double dfXMin = 0.0;
1426 491 : double dfXMax = 0.0;
1427 491 : double dfYMin = 0.0;
1428 491 : double dfYMax = 0.0;
1429 :
1430 491 : OGREnvelope sEnvelope;
1431 :
1432 491 : poGeometry->getEnvelope(&sEnvelope);
1433 491 : if (poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax))
1434 : {
1435 386 : dfXMin = std::min(dfXMin, sEnvelope.MinX);
1436 386 : dfXMax = std::max(dfXMax, sEnvelope.MaxX);
1437 386 : dfYMin = std::min(dfYMin, sEnvelope.MinY);
1438 386 : dfYMax = std::max(dfYMax, sEnvelope.MaxY);
1439 : }
1440 : else
1441 : {
1442 105 : dfXMin = sEnvelope.MinX;
1443 105 : dfXMax = sEnvelope.MaxX;
1444 105 : dfYMin = sEnvelope.MinY;
1445 105 : dfYMax = sEnvelope.MaxY;
1446 : }
1447 :
1448 491 : poClass->SetExtents(dfXMin, dfXMax, dfYMin, dfYMax);
1449 : }
1450 492 : delete poGeometry;
1451 : }
1452 : }
1453 :
1454 530 : delete poFeature;
1455 : }
1456 :
1457 112 : GML_BuildOGRGeometryFromList_DestroyCache(hCacheSRS);
1458 :
1459 112 : if (bGetExtents && m_bCanUseGlobalSRSName && m_pszGlobalSRSName &&
1460 26 : !bFoundPerFeatureSRSName && m_bInvertAxisOrderIfLatLong &&
1461 237 : GML_IsLegitSRSName(m_pszGlobalSRSName) &&
1462 13 : GML_IsSRSLatLongOrder(m_pszGlobalSRSName))
1463 : {
1464 : /* So when we have computed the extent, we didn't know yet */
1465 : /* the SRS to use. Now we know it, we have to fix the extent */
1466 : /* order */
1467 :
1468 6 : for (int i = 0; i < m_nClassCount; i++)
1469 : {
1470 3 : GMLFeatureClass *poClass = m_papoClass[i];
1471 3 : if (poClass->HasExtents())
1472 : {
1473 2 : double dfXMin = 0.0;
1474 2 : double dfXMax = 0.0;
1475 2 : double dfYMin = 0.0;
1476 2 : double dfYMax = 0.0;
1477 2 : if (poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax))
1478 2 : poClass->SetExtents(dfYMin, dfYMax, dfXMin, dfXMax);
1479 : }
1480 : }
1481 : }
1482 :
1483 112 : CleanupParser();
1484 :
1485 112 : return true;
1486 : }
1487 :
1488 : /************************************************************************/
1489 : /* ResetReading() */
1490 : /************************************************************************/
1491 :
1492 734 : void GMLReader::ResetReading()
1493 :
1494 : {
1495 734 : CleanupParser();
1496 734 : SetFilteredClassName(nullptr);
1497 734 : }
1498 :
1499 : /************************************************************************/
1500 : /* SetGlobalSRSName() */
1501 : /************************************************************************/
1502 :
1503 193 : void GMLReader::SetGlobalSRSName(const char *pszGlobalSRSName)
1504 : {
1505 193 : if (m_pszGlobalSRSName == nullptr && pszGlobalSRSName != nullptr)
1506 : {
1507 59 : const char *pszVertCS_EPSG = nullptr;
1508 73 : if (STARTS_WITH(pszGlobalSRSName, "EPSG:") &&
1509 14 : (pszVertCS_EPSG = strstr(pszGlobalSRSName, ", EPSG:")) != nullptr)
1510 : {
1511 2 : m_pszGlobalSRSName =
1512 2 : CPLStrdup(CPLSPrintf("EPSG:%d+%d", atoi(pszGlobalSRSName + 5),
1513 : atoi(pszVertCS_EPSG + 7)));
1514 : }
1515 57 : else if (STARTS_WITH(pszGlobalSRSName, "EPSG:") && m_bConsiderEPSGAsURN)
1516 : {
1517 5 : m_pszGlobalSRSName = CPLStrdup(
1518 : CPLSPrintf("urn:ogc:def:crs:EPSG::%s", pszGlobalSRSName + 5));
1519 : }
1520 : else
1521 : {
1522 52 : m_pszGlobalSRSName = CPLStrdup(pszGlobalSRSName);
1523 : }
1524 59 : m_bCanUseGlobalSRSName = m_pszGlobalSRSName != nullptr;
1525 : }
1526 193 : }
1527 :
1528 : /************************************************************************/
1529 : /* SetFilteredClassName() */
1530 : /************************************************************************/
1531 :
1532 812 : bool GMLReader::SetFilteredClassName(const char *pszClassName)
1533 : {
1534 812 : CPLFree(m_pszFilteredClassName);
1535 812 : m_pszFilteredClassName = pszClassName ? CPLStrdup(pszClassName) : nullptr;
1536 :
1537 812 : m_nFilteredClassIndex = -1;
1538 812 : if (m_pszFilteredClassName != nullptr)
1539 : {
1540 534 : for (int i = 0; i < m_nClassCount; i++)
1541 : {
1542 1002 : if (strcmp(m_papoClass[i]->GetElementName(),
1543 501 : m_pszFilteredClassName) == 0)
1544 : {
1545 45 : m_nFilteredClassIndex = i;
1546 45 : break;
1547 : }
1548 : }
1549 : }
1550 :
1551 812 : return true;
1552 : }
|