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