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