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 462 : IGMLReader::~IGMLReader()
42 : {
43 462 : }
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 457 : 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 457 : bGetSecondaryGeometryOption);
89 : }
90 :
91 : #endif
92 :
93 : CPLMutex *GMLReader::hMutex = nullptr;
94 :
95 : /************************************************************************/
96 : /* GMLReader() */
97 : /************************************************************************/
98 :
99 457 : GMLReader::GMLReader(
100 : #if !defined(HAVE_EXPAT) || !defined(HAVE_XERCES)
101 : CPL_UNUSED
102 : #endif
103 : bool bUseExpatParserPreferably,
104 : bool bInvertAxisOrderIfLatLong, bool bConsiderEPSGAsURN,
105 457 : GMLSwapCoordinatesEnum eSwapCoordinates, bool bGetSecondaryGeometryOption)
106 : : // Experimental. Not publicly advertized. See commented doc in
107 : // drv_gml.html
108 : m_bFetchAllGeometries(
109 457 : 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 914 : CPLTestBool(CPLGetConfigOption("GML_FACE_HOLE_NEGATIVE", "NO")))
118 : {
119 : #ifndef HAVE_XERCES
120 : #else
121 : #ifdef HAVE_EXPAT
122 457 : if (bUseExpatParserPreferably)
123 455 : bUseExpatReader = true;
124 : #endif
125 : #endif
126 :
127 : #if defined(HAVE_EXPAT) && defined(HAVE_XERCES)
128 457 : if (bUseExpatReader)
129 455 : CPLDebug("GML", "Using Expat reader");
130 : else
131 2 : CPLDebug("GML", "Using Xerces reader");
132 : #endif
133 457 : }
134 :
135 : /************************************************************************/
136 : /* ~GMLReader() */
137 : /************************************************************************/
138 :
139 914 : GMLReader::~GMLReader()
140 :
141 : {
142 457 : GMLReader::ClearClasses();
143 :
144 457 : CPLFree(m_pszFilename);
145 :
146 457 : CleanupParser();
147 :
148 457 : delete m_poRecycledState;
149 :
150 : #ifdef HAVE_XERCES
151 457 : if (m_bXercesInitialized)
152 2 : OGRDeinitializeXerces();
153 : #endif
154 : #ifdef HAVE_EXPAT
155 457 : CPLFree(pabyBuf);
156 : #endif
157 :
158 457 : if (fpGML)
159 456 : VSIFCloseL(fpGML);
160 457 : fpGML = nullptr;
161 :
162 457 : CPLFree(m_pszGlobalSRSName);
163 :
164 457 : CPLFree(m_pszFilteredClassName);
165 914 : }
166 :
167 : /************************************************************************/
168 : /* SetSourceFile() */
169 : /************************************************************************/
170 :
171 462 : void GMLReader::SetSourceFile(const char *pszFilename)
172 :
173 : {
174 462 : CPLFree(m_pszFilename);
175 462 : m_pszFilename = CPLStrdup(pszFilename);
176 462 : }
177 :
178 : /************************************************************************/
179 : /* GetSourceFileName() */
180 : /************************************************************************/
181 :
182 456 : const char *GMLReader::GetSourceFileName()
183 : {
184 456 : return m_pszFilename;
185 : }
186 :
187 : /************************************************************************/
188 : /* SetFP() */
189 : /************************************************************************/
190 :
191 438 : void GMLReader::SetFP(VSILFILE *fp)
192 : {
193 438 : fpGML = fp;
194 438 : }
195 :
196 : /************************************************************************/
197 : /* SetupParser() */
198 : /************************************************************************/
199 :
200 605 : bool GMLReader::SetupParser()
201 :
202 : {
203 605 : if (fpGML == nullptr)
204 23 : fpGML = VSIFOpenL(m_pszFilename, "rt");
205 605 : if (fpGML != nullptr)
206 605 : VSIFSeekL(fpGML, 0, SEEK_SET);
207 :
208 605 : int bRet = -1;
209 : #ifdef HAVE_EXPAT
210 605 : if (bUseExpatReader)
211 603 : bRet = SetupParserExpat();
212 : #endif
213 :
214 : #ifdef HAVE_XERCES
215 605 : if (!bUseExpatReader)
216 2 : bRet = SetupParserXerces();
217 : #endif
218 605 : if (bRet < 0)
219 : {
220 0 : CPLError(CE_Failure, CPLE_AppDefined,
221 : "SetupParser(): should not happen");
222 0 : return false;
223 : }
224 :
225 605 : if (!bRet)
226 0 : return false;
227 :
228 605 : m_bReadStarted = false;
229 :
230 : // Push an empty state.
231 605 : PushState(m_poRecycledState ? m_poRecycledState : new GMLReadState());
232 605 : m_poRecycledState = nullptr;
233 :
234 605 : 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 603 : bool GMLReader::SetupParserExpat()
320 : {
321 : // Cleanup any old parser.
322 603 : if (oParser != nullptr)
323 5 : CleanupParser();
324 :
325 603 : oParser = OGRCreateExpatXMLParser();
326 603 : m_poGMLHandler = new GMLExpatHandler(this, oParser);
327 :
328 603 : XML_SetElementHandler(oParser, GMLExpatHandler::startElementCbk,
329 : GMLExpatHandler::endElementCbk);
330 603 : XML_SetCharacterDataHandler(oParser, GMLExpatHandler::dataHandlerCbk);
331 603 : XML_SetUserData(oParser, m_poGMLHandler);
332 :
333 603 : if (pabyBuf == nullptr)
334 359 : pabyBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(PARSER_BUF_SIZE));
335 603 : if (pabyBuf == nullptr)
336 0 : return false;
337 :
338 603 : return true;
339 : }
340 : #endif
341 :
342 : /************************************************************************/
343 : /* CleanupParser() */
344 : /************************************************************************/
345 :
346 1370 : void GMLReader::CleanupParser()
347 :
348 : {
349 : #ifdef HAVE_XERCES
350 1370 : if (!bUseExpatReader && m_poSAXReader == nullptr)
351 3 : return;
352 : #endif
353 :
354 : #ifdef HAVE_EXPAT
355 1367 : if (bUseExpatReader && oParser == nullptr)
356 762 : return;
357 : #endif
358 :
359 1216 : while (m_poState)
360 611 : PopState();
361 :
362 : #ifdef HAVE_XERCES
363 605 : delete m_poSAXReader;
364 605 : m_poSAXReader = nullptr;
365 605 : OGRDestroyXercesInputSource(m_GMLInputSource);
366 605 : m_GMLInputSource = nullptr;
367 605 : delete m_poCompleteFeature;
368 605 : m_poCompleteFeature = nullptr;
369 605 : m_bEOF = false;
370 : #endif
371 :
372 : #ifdef HAVE_EXPAT
373 605 : if (oParser)
374 603 : XML_ParserFree(oParser);
375 605 : oParser = nullptr;
376 :
377 1258 : for (int i = nFeatureTabIndex; i < nFeatureTabLength; i++)
378 653 : delete ppoFeatureTab[i];
379 605 : CPLFree(ppoFeatureTab);
380 605 : nFeatureTabIndex = 0;
381 605 : nFeatureTabLength = 0;
382 605 : nFeatureTabAlloc = 0;
383 605 : ppoFeatureTab = nullptr;
384 605 : m_osErrorMessage.clear();
385 :
386 : #endif
387 :
388 605 : delete m_poGMLHandler;
389 605 : m_poGMLHandler = nullptr;
390 :
391 605 : 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 2837 : GMLFeature *GMLReader::NextFeatureExpat()
456 :
457 : {
458 2837 : if (!m_bReadStarted)
459 : {
460 603 : if (oParser == nullptr)
461 462 : SetupParser();
462 :
463 603 : m_bReadStarted = true;
464 : }
465 :
466 2837 : if (nFeatureTabIndex < nFeatureTabLength)
467 : {
468 1996 : return ppoFeatureTab[nFeatureTabIndex++];
469 : }
470 :
471 841 : 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 1444 : if (fpGML == nullptr || m_bStopParsing || VSIFEofL(fpGML) ||
479 605 : VSIFErrorL(fpGML))
480 234 : return nullptr;
481 :
482 605 : nFeatureTabLength = 0;
483 605 : nFeatureTabIndex = 0;
484 :
485 605 : int nDone = 0;
486 0 : do
487 : {
488 : // Reset counter that is used to detect billion laugh attacks.
489 605 : static_cast<GMLExpatHandler *>(m_poGMLHandler)
490 605 : ->ResetDataHandlerCounter();
491 :
492 : unsigned int nLen = static_cast<unsigned int>(
493 605 : VSIFReadL(pabyBuf, 1, PARSER_BUF_SIZE, fpGML));
494 605 : 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 605 : while (nDone && nLen > 0 && pabyBuf[nLen - 1] == '\0')
501 0 : nLen--;
502 :
503 605 : 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 605 : if (!m_bStopParsing)
516 600 : m_bStopParsing = static_cast<GMLExpatHandler *>(m_poGMLHandler)
517 600 : ->HasStoppedParsing();
518 605 : } while (!nDone && !m_bStopParsing && nFeatureTabLength == 0);
519 :
520 605 : if (nFeatureTabLength)
521 561 : 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 2839 : GMLFeature *GMLReader::NextFeature()
534 : {
535 : #ifdef HAVE_EXPAT
536 2839 : if (bUseExpatReader)
537 2837 : 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 3211 : void GMLReader::PushFeature(const char *pszElement, const char *pszFID,
565 : int nClassIndex)
566 :
567 : {
568 3211 : int iClass = 0;
569 :
570 3211 : if (nClassIndex != INT_MAX)
571 : {
572 1291 : iClass = nClassIndex;
573 : }
574 : else
575 : {
576 : /* --------------------------------------------------------------------
577 : */
578 : /* Find the class of this element. */
579 : /* --------------------------------------------------------------------
580 : */
581 4294 : for (; iClass < m_nClassCount; iClass++)
582 : {
583 4140 : if (EQUAL(pszElement, m_papoClass[iClass]->GetElementName()))
584 1766 : 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 1920 : if (iClass == m_nClassCount)
594 : {
595 154 : CPLAssert(!m_bClassListLocked);
596 :
597 154 : GMLFeatureClass *poNewClass = new GMLFeatureClass(pszElement);
598 :
599 154 : AddClass(poNewClass);
600 : }
601 : }
602 :
603 : /* -------------------------------------------------------------------- */
604 : /* Create a feature of this feature class. Try to set the fid */
605 : /* if available. */
606 : /* -------------------------------------------------------------------- */
607 3211 : GMLFeature *poFeature = new GMLFeature(m_papoClass[iClass]);
608 3211 : if (pszFID != nullptr)
609 : {
610 1675 : poFeature->SetFID(pszFID);
611 : }
612 :
613 : /* -------------------------------------------------------------------- */
614 : /* Create and push a new read state. */
615 : /* -------------------------------------------------------------------- */
616 : GMLReadState *poState =
617 3211 : m_poRecycledState ? m_poRecycledState : new GMLReadState();
618 3211 : m_poRecycledState = nullptr;
619 3211 : poState->m_poFeature = poFeature;
620 3211 : PushState(poState);
621 3211 : }
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 6147 : int GMLReader::GetFeatureElementIndex(const char *pszElement,
631 : int nElementLength,
632 : GMLAppSchemaType eAppSchemaType)
633 :
634 : {
635 6147 : const char *pszLast = m_poState->GetLastComponent();
636 6147 : const size_t nLenLast = m_poState->GetLastComponentLen();
637 :
638 6147 : if (eAppSchemaType == APPSCHEMA_MTKGML)
639 : {
640 44 : if (m_poState->m_nPathLength != 1)
641 22 : return -1;
642 : }
643 6103 : else if ((nLenLast >= 6 && EQUAL(pszLast + nLenLast - 6, "member")) ||
644 1965 : (nLenLast >= 7 && EQUAL(pszLast + nLenLast - 7, "members")))
645 : {
646 : // Default feature name.
647 : }
648 : else
649 : {
650 2757 : if (nLenLast == 4 && strcmp(pszLast, "dane") == 0)
651 : {
652 : // Polish TBD GML.
653 : }
654 :
655 : // Begin of OpenLS.
656 2757 : else if (nLenLast == 19 && nElementLength == 15 &&
657 1 : strcmp(pszLast, "GeocodeResponseList") == 0 &&
658 0 : strcmp(pszElement, "GeocodedAddress") == 0)
659 : {
660 : }
661 2757 : 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 2754 : 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 2745 : else if (nLenLast > 6 &&
678 517 : 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 2745 : 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 2741 : 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 2737 : 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 2693 : if (m_bClassListLocked)
709 : {
710 12427 : for (int i = 0; i < m_nClassCount; i++)
711 : {
712 10864 : if (m_poState->osPath.size() + 1 + nElementLength ==
713 10864 : 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 11125 : 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 1049 : return -1;
736 : }
737 : }
738 : }
739 :
740 : // If the class list isn't locked, any element that is a featureMember
741 : // will do.
742 4994 : if (!m_bClassListLocked)
743 1920 : return INT_MAX;
744 :
745 : // otherwise, find a class with the desired element name.
746 17270 : for (int i = 0; i < m_nClassCount; i++)
747 : {
748 31404 : if (nElementLength ==
749 18110 : static_cast<int>(m_papoClass[i]->GetElementNameLen()) &&
750 2408 : memcmp(pszElement, m_papoClass[i]->GetElementName(),
751 : nElementLength) == 0)
752 1506 : return i;
753 : }
754 :
755 1568 : return -1;
756 : }
757 :
758 : /************************************************************************/
759 : /* IsCityGMLGenericAttributeElement() */
760 : /************************************************************************/
761 :
762 136 : bool GMLReader::IsCityGMLGenericAttributeElement(const char *pszElement,
763 : void *attr)
764 :
765 : {
766 136 : if (strcmp(pszElement, "stringAttribute") != 0 &&
767 98 : strcmp(pszElement, "intAttribute") != 0 &&
768 96 : strcmp(pszElement, "doubleAttribute") != 0)
769 94 : 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 13529 : int GMLReader::GetAttributeElementIndex(const char *pszElement, int nLen,
803 : const char *pszAttrKey)
804 :
805 : {
806 13529 : 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 13529 : if (!poClass->IsSchemaLocked())
811 7766 : return INT_MAX;
812 :
813 : // Otherwise build the path to this element into a single string
814 : // and compare against known attributes.
815 5763 : if (m_poState->m_nPathLength == 0)
816 : {
817 3897 : if (pszAttrKey == nullptr)
818 3820 : return poClass->GetPropertyIndexBySrcElement(pszElement, nLen);
819 : else
820 : {
821 77 : int nFullLen = nLen + 1 + static_cast<int>(strlen(pszAttrKey));
822 77 : osElemPath.reserve(nFullLen);
823 77 : osElemPath.assign(pszElement, nLen);
824 77 : osElemPath.append(1, '@');
825 77 : osElemPath.append(pszAttrKey);
826 77 : return poClass->GetPropertyIndexBySrcElement(osElemPath.c_str(),
827 77 : nFullLen);
828 : }
829 : }
830 : else
831 : {
832 1866 : int nFullLen = nLen + static_cast<int>(m_poState->osPath.size()) + 1;
833 1866 : if (pszAttrKey != nullptr)
834 89 : nFullLen += 1 + static_cast<int>(strlen(pszAttrKey));
835 1866 : osElemPath.reserve(nFullLen);
836 1866 : osElemPath.assign(m_poState->osPath);
837 1866 : osElemPath.append(1, '|');
838 1866 : osElemPath.append(pszElement, nLen);
839 1866 : if (pszAttrKey != nullptr)
840 : {
841 89 : osElemPath.append(1, '@');
842 89 : osElemPath.append(pszAttrKey);
843 : }
844 1866 : return poClass->GetPropertyIndexBySrcElement(osElemPath.c_str(),
845 1866 : nFullLen);
846 : }
847 : }
848 :
849 : /************************************************************************/
850 : /* PopState() */
851 : /************************************************************************/
852 :
853 3816 : void GMLReader::PopState()
854 :
855 : {
856 3816 : if (m_poState != nullptr)
857 : {
858 : #ifdef HAVE_XERCES
859 3816 : 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 3815 : 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 3816 : if (bUseExpatReader && m_poState->m_poFeature != nullptr)
874 : {
875 3210 : if (nFeatureTabLength >= nFeatureTabAlloc)
876 : {
877 630 : nFeatureTabAlloc = nFeatureTabLength * 4 / 3 + 16;
878 630 : ppoFeatureTab = static_cast<GMLFeature **>(CPLRealloc(
879 630 : ppoFeatureTab, sizeof(GMLFeature *) * (nFeatureTabAlloc)));
880 : }
881 3210 : ppoFeatureTab[nFeatureTabLength] = m_poState->m_poFeature;
882 3210 : nFeatureTabLength++;
883 :
884 3210 : m_poState->m_poFeature = nullptr;
885 : }
886 : #endif
887 :
888 3816 : GMLReadState *poParent = m_poState->m_poParentState;
889 :
890 3816 : delete m_poRecycledState;
891 3816 : m_poRecycledState = m_poState;
892 3816 : m_poRecycledState->Reset();
893 3816 : m_poState = poParent;
894 : }
895 3816 : }
896 :
897 : /************************************************************************/
898 : /* PushState() */
899 : /************************************************************************/
900 :
901 3816 : void GMLReader::PushState(GMLReadState *poState)
902 :
903 : {
904 3816 : poState->m_poParentState = m_poState;
905 3816 : m_poState = poState;
906 3816 : }
907 :
908 : /************************************************************************/
909 : /* GetClass() */
910 : /************************************************************************/
911 :
912 911 : GMLFeatureClass *GMLReader::GetClass(int iClass) const
913 :
914 : {
915 911 : if (iClass < 0 || iClass >= m_nClassCount)
916 0 : return nullptr;
917 :
918 911 : return m_papoClass[iClass];
919 : }
920 :
921 : /************************************************************************/
922 : /* GetClass() */
923 : /************************************************************************/
924 :
925 1724 : GMLFeatureClass *GMLReader::GetClass(const char *pszName) const
926 :
927 : {
928 2884 : for (int iClass = 0; iClass < m_nClassCount; iClass++)
929 : {
930 1777 : if (EQUAL(m_papoClass[iClass]->GetName(), pszName))
931 617 : return m_papoClass[iClass];
932 : }
933 :
934 1107 : return nullptr;
935 : }
936 :
937 : /************************************************************************/
938 : /* AddClass() */
939 : /************************************************************************/
940 :
941 709 : int GMLReader::AddClass(GMLFeatureClass *poNewClass)
942 :
943 : {
944 709 : CPLAssert(GetClass(poNewClass->GetName()) == nullptr);
945 :
946 709 : m_nClassCount++;
947 709 : m_papoClass = static_cast<GMLFeatureClass **>(
948 709 : CPLRealloc(m_papoClass, sizeof(void *) * m_nClassCount));
949 709 : m_papoClass[m_nClassCount - 1] = poNewClass;
950 :
951 709 : if (poNewClass->HasFeatureProperties())
952 8 : m_bLookForClassAtAnyLevel = true;
953 :
954 709 : return m_nClassCount - 1;
955 : }
956 :
957 : /************************************************************************/
958 : /* ClearClasses() */
959 : /************************************************************************/
960 :
961 626 : void GMLReader::ClearClasses()
962 :
963 : {
964 1327 : for (int i = 0; i < m_nClassCount; i++)
965 701 : delete m_papoClass[i];
966 626 : CPLFree(m_papoClass);
967 :
968 626 : m_nClassCount = 0;
969 626 : m_papoClass = nullptr;
970 626 : m_bLookForClassAtAnyLevel = false;
971 626 : }
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 8266 : void GMLReader::SetFeaturePropertyDirectly(const char *pszElement,
982 : char *pszValue, int iPropertyIn,
983 : GMLPropertyType eType)
984 :
985 : {
986 8266 : GMLFeature *poFeature = GetState()->m_poFeature;
987 :
988 8266 : CPLAssert(poFeature != nullptr);
989 :
990 : /* -------------------------------------------------------------------- */
991 : /* Does this property exist in the feature class? If not, add */
992 : /* it. */
993 : /* -------------------------------------------------------------------- */
994 8266 : GMLFeatureClass *poClass = poFeature->GetClass();
995 8266 : int iProperty = 0;
996 :
997 8266 : const int nPropertyCount = poClass->GetPropertyCount();
998 8266 : if (iPropertyIn >= 0 && iPropertyIn < nPropertyCount)
999 : {
1000 3109 : iProperty = iPropertyIn;
1001 : }
1002 : else
1003 : {
1004 13716 : for (; iProperty < nPropertyCount; iProperty++)
1005 : {
1006 13261 : if (strcmp(poClass->GetProperty(iProperty)->GetSrcElement(),
1007 13261 : pszElement) == 0)
1008 4702 : break;
1009 : }
1010 :
1011 5157 : if (iProperty == nPropertyCount)
1012 : {
1013 455 : 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 454 : CPLString osFieldName;
1023 :
1024 454 : 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 442 : else if (strchr(pszElement, '|') == nullptr)
1047 : {
1048 342 : osFieldName = pszElement;
1049 : }
1050 : else
1051 : {
1052 100 : osFieldName = strrchr(pszElement, '|') + 1;
1053 100 : if (poClass->GetPropertyIndex(osFieldName) != -1)
1054 12 : osFieldName = pszElement;
1055 : }
1056 :
1057 454 : size_t nPos = osFieldName.find("@");
1058 454 : if (nPos != std::string::npos)
1059 3 : osFieldName[nPos] = '_';
1060 :
1061 : // Does this conflict with an existing property name?
1062 455 : 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 454 : new GMLPropertyDefn(osFieldName, pszElement);
1076 :
1077 454 : if (EQUAL(CPLGetConfigOption("GML_FIELDTYPES", ""),
1078 : "ALWAYS_STRING"))
1079 0 : poPDefn->SetType(GMLPT_String);
1080 454 : else if (eType != GMLPT_Untyped)
1081 3 : poPDefn->SetType(eType);
1082 :
1083 454 : 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 8265 : poFeature->SetPropertyDirectly(iProperty, pszValue);
1096 :
1097 : /* -------------------------------------------------------------------- */
1098 : /* Do we need to update the property type? */
1099 : /* -------------------------------------------------------------------- */
1100 8265 : if (!poClass->IsSchemaLocked() && !EQUAL(pszValue, OGR_GML_NULL))
1101 : {
1102 5014 : auto poClassProperty = poClass->GetProperty(iProperty);
1103 5014 : if (poClassProperty)
1104 : {
1105 5014 : const GMLProperty *poProp = poFeature->GetProperty(iProperty);
1106 5014 : if (poProp)
1107 : {
1108 5014 : poClassProperty->AnalysePropertyValue(poProp, m_bSetWidthFlag);
1109 : }
1110 : }
1111 : else
1112 : {
1113 0 : CPLAssert(false);
1114 : }
1115 : }
1116 : }
1117 :
1118 : /************************************************************************/
1119 : /* LoadClasses() */
1120 : /************************************************************************/
1121 :
1122 62 : bool GMLReader::LoadClasses(const char *pszFile)
1123 :
1124 : {
1125 : // Add logic later to determine reasonable default schema file.
1126 62 : if (pszFile == nullptr)
1127 0 : return false;
1128 :
1129 : /* -------------------------------------------------------------------- */
1130 : /* Load the raw XML file. */
1131 : /* -------------------------------------------------------------------- */
1132 62 : GByte *pabyRet = nullptr;
1133 62 : const char *pszWholeText = nullptr;
1134 : {
1135 : #ifdef EMBED_RESOURCE_FILES
1136 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1137 : #endif
1138 62 : if (VSIIngestFile(nullptr, pszFile, &pabyRet, nullptr,
1139 62 : 100 * 1024 * 1024))
1140 : {
1141 62 : pszWholeText = reinterpret_cast<const char *>(pabyRet);
1142 : }
1143 : else
1144 : {
1145 : #ifdef EMBED_RESOURCE_FILES
1146 : pszWholeText = GMLGetFileContent(pszFile);
1147 : if (pszWholeText)
1148 : {
1149 : CPLDebug("GML", "Using embedded %s", pszFile);
1150 : }
1151 : #endif
1152 : }
1153 : }
1154 62 : if (!pszWholeText)
1155 0 : return false;
1156 :
1157 62 : if (strstr(pszWholeText, "<GMLFeatureClassList") == nullptr)
1158 : {
1159 0 : VSIFree(pabyRet);
1160 0 : CPLError(CE_Failure, CPLE_AppDefined,
1161 : "File %s does not contain a GMLFeatureClassList tree.",
1162 : pszFile);
1163 0 : return false;
1164 : }
1165 :
1166 : /* -------------------------------------------------------------------- */
1167 : /* Convert to XML parse tree. */
1168 : /* -------------------------------------------------------------------- */
1169 124 : CPLXMLTreeCloser psRoot(CPLParseXMLString(pszWholeText));
1170 62 : VSIFree(pabyRet);
1171 :
1172 : // We assume parser will report errors via CPL.
1173 62 : if (psRoot.get() == nullptr)
1174 0 : return false;
1175 :
1176 122 : if (psRoot->eType != CXT_Element ||
1177 60 : !EQUAL(psRoot->pszValue, "GMLFeatureClassList"))
1178 : {
1179 2 : CPLError(CE_Failure, CPLE_AppDefined,
1180 : "File %s is not a GMLFeatureClassList document.", pszFile);
1181 2 : return false;
1182 : }
1183 :
1184 : const char *pszSequentialLayers =
1185 60 : CPLGetXMLValue(psRoot.get(), "SequentialLayers", nullptr);
1186 60 : if (pszSequentialLayers)
1187 10 : m_nHasSequentialLayers = CPLTestBool(pszSequentialLayers);
1188 :
1189 : /* -------------------------------------------------------------------- */
1190 : /* Extract feature classes for all definitions found. */
1191 : /* -------------------------------------------------------------------- */
1192 208 : for (CPLXMLNode *psThis = psRoot->psChild; psThis != nullptr;
1193 148 : psThis = psThis->psNext)
1194 : {
1195 148 : if (psThis->eType == CXT_Element &&
1196 121 : EQUAL(psThis->pszValue, "GMLFeatureClass"))
1197 : {
1198 111 : GMLFeatureClass *poClass = new GMLFeatureClass();
1199 :
1200 111 : if (!poClass->InitializeFromXML(psThis))
1201 : {
1202 0 : delete poClass;
1203 0 : return false;
1204 : }
1205 :
1206 111 : poClass->SetSchemaLocked(true);
1207 :
1208 111 : AddClass(poClass);
1209 : }
1210 : }
1211 :
1212 60 : SetClassListLocked(true);
1213 :
1214 60 : return true;
1215 : }
1216 :
1217 : /************************************************************************/
1218 : /* SaveClasses() */
1219 : /************************************************************************/
1220 :
1221 112 : bool GMLReader::SaveClasses(const char *pszFile)
1222 :
1223 : {
1224 : // Add logic later to determine reasonable default schema file.
1225 112 : if (pszFile == nullptr)
1226 0 : return false;
1227 :
1228 : /* -------------------------------------------------------------------- */
1229 : /* Create in memory schema tree. */
1230 : /* -------------------------------------------------------------------- */
1231 : CPLXMLNode *psRoot =
1232 112 : CPLCreateXMLNode(nullptr, CXT_Element, "GMLFeatureClassList");
1233 :
1234 112 : if (m_nHasSequentialLayers != -1 && m_nClassCount > 1)
1235 : {
1236 18 : CPLCreateXMLElementAndValue(psRoot, "SequentialLayers",
1237 18 : m_nHasSequentialLayers ? "true" : "false");
1238 : }
1239 :
1240 248 : for (int iClass = 0; iClass < m_nClassCount; iClass++)
1241 : {
1242 136 : CPLAddXMLChild(psRoot, m_papoClass[iClass]->SerializeToXML());
1243 : }
1244 :
1245 : /* -------------------------------------------------------------------- */
1246 : /* Serialize to disk. */
1247 : /* -------------------------------------------------------------------- */
1248 112 : char *pszWholeText = CPLSerializeXMLTree(psRoot);
1249 :
1250 112 : CPLDestroyXMLNode(psRoot);
1251 :
1252 112 : auto fp = VSIVirtualHandleUniquePtr(VSIFOpenL(pszFile, "wb"));
1253 :
1254 112 : bool bSuccess = true;
1255 112 : if (fp == nullptr)
1256 0 : bSuccess = false;
1257 : else
1258 : {
1259 112 : if (fp->Write(pszWholeText, strlen(pszWholeText), 1) != 1)
1260 0 : bSuccess = false;
1261 : }
1262 :
1263 112 : CPLFree(pszWholeText);
1264 :
1265 112 : return bSuccess;
1266 : }
1267 :
1268 : /************************************************************************/
1269 : /* PrescanForSchema() */
1270 : /* */
1271 : /* For now we use a pretty dumb approach of just doing a normal */
1272 : /* scan of the whole file, building up the schema information. */
1273 : /* Eventually we hope to do a more efficient scan when just */
1274 : /* looking for schema information. */
1275 : /************************************************************************/
1276 :
1277 131 : bool GMLReader::PrescanForSchema(bool bGetExtents, bool bOnlyDetectSRS)
1278 :
1279 : {
1280 131 : if (m_pszFilename == nullptr)
1281 0 : return false;
1282 :
1283 131 : if (!bOnlyDetectSRS)
1284 : {
1285 122 : SetClassListLocked(false);
1286 122 : ClearClasses();
1287 : }
1288 :
1289 131 : if (!SetupParser())
1290 0 : return false;
1291 :
1292 131 : m_bCanUseGlobalSRSName = true;
1293 :
1294 131 : GMLFeatureClass *poLastClass = nullptr;
1295 :
1296 131 : m_nHasSequentialLayers = TRUE;
1297 :
1298 : std::unique_ptr<OGRGML_SRSCache, decltype(&OGRGML_SRSCache_Destroy)>
1299 262 : poSRSCache{OGRGML_SRSCache_Create(), OGRGML_SRSCache_Destroy};
1300 :
1301 262 : std::string osWork;
1302 :
1303 140 : for (int i = 0; i < m_nClassCount; i++)
1304 : {
1305 9 : m_papoClass[i]->SetFeatureCount(-1);
1306 9 : m_papoClass[i]->SetSRSName(nullptr);
1307 : }
1308 :
1309 131 : GMLFeature *poFeature = nullptr;
1310 131 : std::set<GMLFeatureClass *> knownClasses;
1311 131 : bool bFoundPerFeatureSRSName = false;
1312 :
1313 875 : while ((poFeature = NextFeature()) != nullptr)
1314 : {
1315 744 : GMLFeatureClass *poClass = poFeature->GetClass();
1316 :
1317 744 : if (knownClasses.find(poClass) == knownClasses.end())
1318 : {
1319 150 : knownClasses.insert(poClass);
1320 150 : if (m_pszGlobalSRSName && GML_IsLegitSRSName(m_pszGlobalSRSName))
1321 : {
1322 46 : poClass->SetSRSName(m_pszGlobalSRSName);
1323 : }
1324 : }
1325 :
1326 779 : if (poLastClass != nullptr && poClass != poLastClass &&
1327 35 : poClass->GetFeatureCount() != -1)
1328 0 : m_nHasSequentialLayers = false;
1329 744 : poLastClass = poClass;
1330 :
1331 744 : if (poClass->GetFeatureCount() == -1)
1332 150 : poClass->SetFeatureCount(1);
1333 : else
1334 594 : poClass->SetFeatureCount(poClass->GetFeatureCount() + 1);
1335 :
1336 744 : const CPLXMLNode *const *papsGeometry = poFeature->GetGeometryList();
1337 744 : bool bGeometryColumnJustCreated = false;
1338 :
1339 744 : const CPLXMLNode *apsGeometries[2] = {nullptr, nullptr};
1340 : const CPLXMLNode *psBoundedByGeometry =
1341 744 : poFeature->GetBoundedByGeometry();
1342 744 : int nFeatureGeomCount = poFeature->GetGeometryCount();
1343 744 : if (psBoundedByGeometry && nFeatureGeomCount == 0 &&
1344 3 : strcmp(psBoundedByGeometry->pszValue, "null") != 0)
1345 : {
1346 1 : apsGeometries[0] = psBoundedByGeometry;
1347 1 : papsGeometry = apsGeometries;
1348 1 : nFeatureGeomCount = 1;
1349 : }
1350 :
1351 744 : if (!bOnlyDetectSRS && papsGeometry != nullptr &&
1352 725 : papsGeometry[0] != nullptr)
1353 : {
1354 668 : if (poClass->GetGeometryPropertyCount() == 0)
1355 : {
1356 234 : std::string osPath(poClass->GetSingleGeomElemPath());
1357 145 : if (osPath.empty() &&
1358 145 : poClass->IsConsistentSingleGeomElemPath() &&
1359 24 : papsGeometry[0] == psBoundedByGeometry)
1360 : {
1361 1 : osPath = "boundedBy";
1362 : }
1363 117 : std::string osGeomName(osPath);
1364 117 : const auto nPos = osGeomName.rfind('|');
1365 117 : if (nPos != std::string::npos)
1366 1 : osGeomName = osGeomName.substr(nPos + 1);
1367 117 : bGeometryColumnJustCreated = true;
1368 234 : poClass->AddGeometryProperty(new GMLGeometryPropertyDefn(
1369 234 : osGeomName.c_str(), osPath.c_str(), wkbUnknown, -1, true));
1370 : }
1371 : }
1372 :
1373 744 : if (bGetExtents && papsGeometry != nullptr)
1374 : {
1375 : const int nIters = std::min(nFeatureGeomCount,
1376 744 : poClass->GetGeometryPropertyCount());
1377 1439 : for (int i = 0; i < nIters; ++i)
1378 : {
1379 695 : if (papsGeometry[i] == nullptr)
1380 8 : continue;
1381 :
1382 690 : const CPLXMLNode *myGeometryList[2] = {papsGeometry[i],
1383 690 : nullptr};
1384 2070 : OGRGeometry *poGeometry = GML_BuildOGRGeometryFromList(
1385 690 : myGeometryList, true, m_bInvertAxisOrderIfLatLong, nullptr,
1386 690 : m_bConsiderEPSGAsURN, m_eSwapCoordinates,
1387 690 : m_bGetSecondaryGeometryOption, poSRSCache.get(),
1388 690 : m_bFaceHoleNegative);
1389 690 : if (poGeometry == nullptr)
1390 3 : continue;
1391 :
1392 687 : auto poGeomProperty = poClass->GetGeometryProperty(i);
1393 : OGRwkbGeometryType eGType =
1394 687 : static_cast<OGRwkbGeometryType>(poGeomProperty->GetType());
1395 :
1396 1374 : const char *pszSRSName = GML_ExtractSrsNameFromGeometry(
1397 687 : myGeometryList, osWork, m_bConsiderEPSGAsURN);
1398 687 : if (pszSRSName != nullptr)
1399 145 : bFoundPerFeatureSRSName = true;
1400 :
1401 687 : if (pszSRSName != nullptr && m_pszGlobalSRSName != nullptr &&
1402 73 : !EQUAL(pszSRSName, m_pszGlobalSRSName))
1403 : {
1404 0 : m_bCanUseGlobalSRSName = false;
1405 : }
1406 687 : if (m_pszGlobalSRSName == nullptr || pszSRSName != nullptr)
1407 : {
1408 247 : if (poClass->GetGeometryPropertyCount() == 1)
1409 238 : poClass->MergeSRSName(pszSRSName);
1410 : else
1411 9 : poGeomProperty->MergeSRSName(pszSRSName ? pszSRSName
1412 : : "");
1413 : }
1414 :
1415 : // Merge geometry type into layer.
1416 687 : if (bGeometryColumnJustCreated)
1417 : {
1418 114 : poGeomProperty->SetType(poGeometry->getGeometryType());
1419 : }
1420 : else
1421 : {
1422 573 : poGeomProperty->SetType(OGRMergeGeometryTypesEx(
1423 573 : eGType, poGeometry->getGeometryType(), true));
1424 : }
1425 :
1426 : // Merge extents.
1427 687 : if (!poGeometry->IsEmpty())
1428 : {
1429 686 : double dfXMin = 0.0;
1430 686 : double dfXMax = 0.0;
1431 686 : double dfYMin = 0.0;
1432 686 : double dfYMax = 0.0;
1433 :
1434 686 : OGREnvelope sEnvelope;
1435 :
1436 686 : poGeometry->getEnvelope(&sEnvelope);
1437 686 : if (poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax))
1438 : {
1439 563 : dfXMin = std::min(dfXMin, sEnvelope.MinX);
1440 563 : dfXMax = std::max(dfXMax, sEnvelope.MaxX);
1441 563 : dfYMin = std::min(dfYMin, sEnvelope.MinY);
1442 563 : dfYMax = std::max(dfYMax, sEnvelope.MaxY);
1443 : }
1444 : else
1445 : {
1446 123 : dfXMin = sEnvelope.MinX;
1447 123 : dfXMax = sEnvelope.MaxX;
1448 123 : dfYMin = sEnvelope.MinY;
1449 123 : dfYMax = sEnvelope.MaxY;
1450 : }
1451 :
1452 686 : poClass->SetExtents(dfXMin, dfXMax, dfYMin, dfYMax);
1453 : }
1454 687 : delete poGeometry;
1455 : }
1456 : }
1457 :
1458 744 : delete poFeature;
1459 : }
1460 :
1461 131 : if (bGetExtents && m_bCanUseGlobalSRSName && m_pszGlobalSRSName &&
1462 30 : !bFoundPerFeatureSRSName && m_bInvertAxisOrderIfLatLong &&
1463 279 : GML_IsLegitSRSName(m_pszGlobalSRSName) &&
1464 17 : GML_IsSRSLatLongOrder(m_pszGlobalSRSName))
1465 : {
1466 : /* So when we have computed the extent, we didn't know yet */
1467 : /* the SRS to use. Now we know it, we have to fix the extent */
1468 : /* order */
1469 :
1470 6 : for (int i = 0; i < m_nClassCount; i++)
1471 : {
1472 3 : GMLFeatureClass *poClass = m_papoClass[i];
1473 3 : if (poClass->HasExtents())
1474 : {
1475 2 : double dfXMin = 0.0;
1476 2 : double dfXMax = 0.0;
1477 2 : double dfYMin = 0.0;
1478 2 : double dfYMax = 0.0;
1479 2 : if (poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax))
1480 2 : poClass->SetExtents(dfYMin, dfYMax, dfXMin, dfXMax);
1481 : }
1482 : }
1483 : }
1484 :
1485 131 : CleanupParser();
1486 :
1487 131 : return true;
1488 : }
1489 :
1490 : /************************************************************************/
1491 : /* ResetReading() */
1492 : /************************************************************************/
1493 :
1494 772 : void GMLReader::ResetReading()
1495 :
1496 : {
1497 772 : CleanupParser();
1498 772 : SetFilteredClassName(nullptr);
1499 772 : }
1500 :
1501 : /************************************************************************/
1502 : /* SetGlobalSRSName() */
1503 : /************************************************************************/
1504 :
1505 209 : void GMLReader::SetGlobalSRSName(const char *pszGlobalSRSName)
1506 : {
1507 209 : if (m_pszGlobalSRSName == nullptr && pszGlobalSRSName != nullptr)
1508 : {
1509 65 : const char *pszVertCS_EPSG = nullptr;
1510 79 : if (STARTS_WITH(pszGlobalSRSName, "EPSG:") &&
1511 14 : (pszVertCS_EPSG = strstr(pszGlobalSRSName, ", EPSG:")) != nullptr)
1512 : {
1513 2 : m_pszGlobalSRSName =
1514 2 : CPLStrdup(CPLSPrintf("EPSG:%d+%d", atoi(pszGlobalSRSName + 5),
1515 : atoi(pszVertCS_EPSG + 7)));
1516 : }
1517 63 : else if (STARTS_WITH(pszGlobalSRSName, "EPSG:") && m_bConsiderEPSGAsURN)
1518 : {
1519 5 : m_pszGlobalSRSName = CPLStrdup(
1520 : CPLSPrintf("urn:ogc:def:crs:EPSG::%s", pszGlobalSRSName + 5));
1521 : }
1522 : else
1523 : {
1524 58 : m_pszGlobalSRSName = CPLStrdup(pszGlobalSRSName);
1525 : }
1526 65 : m_bCanUseGlobalSRSName = m_pszGlobalSRSName != nullptr;
1527 : }
1528 209 : }
1529 :
1530 : /************************************************************************/
1531 : /* SetFilteredClassName() */
1532 : /************************************************************************/
1533 :
1534 850 : bool GMLReader::SetFilteredClassName(const char *pszClassName)
1535 : {
1536 850 : CPLFree(m_pszFilteredClassName);
1537 850 : m_pszFilteredClassName = pszClassName ? CPLStrdup(pszClassName) : nullptr;
1538 :
1539 850 : m_nFilteredClassIndex = -1;
1540 850 : if (m_pszFilteredClassName != nullptr)
1541 : {
1542 534 : for (int i = 0; i < m_nClassCount; i++)
1543 : {
1544 1002 : if (strcmp(m_papoClass[i]->GetElementName(),
1545 501 : m_pszFilteredClassName) == 0)
1546 : {
1547 45 : m_nFilteredClassIndex = i;
1548 45 : break;
1549 : }
1550 : }
1551 : }
1552 :
1553 850 : return true;
1554 : }
|