EVOLUTION-MANAGER
Edit File: gmlreader.cpp
/****************************************************************************** * $Id: gmlreader.cpp 27729 2014-09-24 00:40:16Z goatbar $ * * Project: GML Reader * Purpose: Implementation of GMLReader class. * Author: Frank Warmerdam, warmerdam@pobox.com * ****************************************************************************** * Copyright (c) 2002, Frank Warmerdam * Copyright (c) 2008-2014, Even Rouault <even dot rouault at mines-paris dot org> * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************/ #include "gmlreader.h" #include "cpl_error.h" #include "cpl_string.h" #include "gmlutils.h" #include "gmlreaderp.h" #include "cpl_conv.h" #include <map> #include "cpl_multiproc.h" #define SUPPORT_GEOMETRY #ifdef SUPPORT_GEOMETRY # include "ogr_geometry.h" #endif /************************************************************************/ /* ~IGMLReader() */ /************************************************************************/ IGMLReader::~IGMLReader() { } /************************************************************************/ /* ==================================================================== */ /* No XERCES or EXPAT Library */ /* ==================================================================== */ /************************************************************************/ #if !defined(HAVE_XERCES) && !defined(HAVE_EXPAT) /************************************************************************/ /* CreateGMLReader() */ /************************************************************************/ IGMLReader *CreateGMLReader(int bUseExpatParserPreferably, int bInvertAxisOrderIfLatLong, int bConsiderEPSGAsURN, int bGetSecondaryGeometryOption) { CPLError( CE_Failure, CPLE_AppDefined, "Unable to create Xerces C++ or Expat based GML reader, Xerces or Expat support\n" "not configured into GDAL/OGR." ); return NULL; } /************************************************************************/ /* ==================================================================== */ /* With XERCES or EXPAT Library */ /* ==================================================================== */ /************************************************************************/ #else /* defined(HAVE_XERCES) || defined(HAVE_EXPAT) */ /************************************************************************/ /* CreateGMLReader() */ /************************************************************************/ IGMLReader *CreateGMLReader(int bUseExpatParserPreferably, int bInvertAxisOrderIfLatLong, int bConsiderEPSGAsURN, int bGetSecondaryGeometryOption) { return new GMLReader(bUseExpatParserPreferably, bInvertAxisOrderIfLatLong, bConsiderEPSGAsURN, bGetSecondaryGeometryOption); } #endif OGRGMLXercesState GMLReader::m_eXercesInitState = OGRGML_XERCES_UNINITIALIZED; int GMLReader::m_nInstanceCount = 0; void *GMLReader::hMutex = NULL; /************************************************************************/ /* GMLReader() */ /************************************************************************/ GMLReader::GMLReader( #ifndef HAVE_XERCES CPL_UNUSED #endif int bUseExpatParserPreferably, int bInvertAxisOrderIfLatLong, int bConsiderEPSGAsURN, int bGetSecondaryGeometryOption) { #ifndef HAVE_XERCES bUseExpatReader = TRUE; #else bUseExpatReader = FALSE; # ifdef HAVE_EXPAT if(bUseExpatParserPreferably) bUseExpatReader = TRUE; # endif #endif #if defined(HAVE_EXPAT) && defined(HAVE_XERCES) if (bUseExpatReader) CPLDebug("GML", "Using Expat reader"); else CPLDebug("GML", "Using Xerces reader"); #endif m_nClassCount = 0; m_papoClass = NULL; m_bLookForClassAtAnyLevel = FALSE; m_bClassListLocked = FALSE; m_poGMLHandler = NULL; #ifdef HAVE_XERCES m_poSAXReader = NULL; m_poCompleteFeature = NULL; m_GMLInputSource = NULL; m_bEOF = FALSE; #endif #ifdef HAVE_EXPAT oParser = NULL; ppoFeatureTab = NULL; nFeatureTabIndex = 0; nFeatureTabLength = 0; nFeatureTabAlloc = 0; pabyBuf = NULL; #endif fpGML = NULL; m_bReadStarted = FALSE; m_poState = NULL; m_poRecycledState = NULL; m_pszFilename = NULL; m_bStopParsing = FALSE; /* A bit experimental. Not publicly advertized. See commented doc in drv_gml.html */ m_bFetchAllGeometries = CSLTestBoolean(CPLGetConfigOption("GML_FETCH_ALL_GEOMETRIES", "NO")); m_bInvertAxisOrderIfLatLong = bInvertAxisOrderIfLatLong; m_bConsiderEPSGAsURN = bConsiderEPSGAsURN; m_bGetSecondaryGeometryOption = bGetSecondaryGeometryOption; m_pszGlobalSRSName = NULL; m_bCanUseGlobalSRSName = FALSE; m_pszFilteredClassName = NULL; m_nFilteredClassIndex = -1; m_bSequentialLayers = -1; /* Must be in synced in OGR_G_CreateFromGML(), OGRGMLLayer::OGRGMLLayer() and GMLReader::GMLReader() */ m_bFaceHoleNegative = CSLTestBoolean(CPLGetConfigOption("GML_FACE_HOLE_NEGATIVE", "NO")); m_bSetWidthFlag = TRUE; m_bReportAllAttributes = CSLTestBoolean( CPLGetConfigOption("GML_ATTRIBUTES_TO_OGR_FIELDS", "NO")); } /************************************************************************/ /* ~GMLReader() */ /************************************************************************/ GMLReader::~GMLReader() { ClearClasses(); CPLFree( m_pszFilename ); CleanupParser(); delete m_poRecycledState; #ifdef HAVE_XERCES { CPLMutexHolderD(&hMutex); --m_nInstanceCount; if( m_nInstanceCount == 0 && m_eXercesInitState == OGRGML_XERCES_INIT_SUCCESSFUL ) { XMLPlatformUtils::Terminate(); m_eXercesInitState = OGRGML_XERCES_UNINITIALIZED; } } #endif #ifdef HAVE_EXPAT CPLFree(pabyBuf); #endif if (fpGML) VSIFCloseL(fpGML); fpGML = NULL; CPLFree(m_pszGlobalSRSName); CPLFree(m_pszFilteredClassName); } /************************************************************************/ /* SetSourceFile() */ /************************************************************************/ void GMLReader::SetSourceFile( const char *pszFilename ) { CPLFree( m_pszFilename ); m_pszFilename = CPLStrdup( pszFilename ); } /************************************************************************/ /* GetSourceFileName() */ /************************************************************************/ const char* GMLReader::GetSourceFileName() { return m_pszFilename; } /************************************************************************/ /* SetFP() */ /************************************************************************/ void GMLReader::SetFP( VSILFILE* fp ) { fpGML = fp; } /************************************************************************/ /* SetupParser() */ /************************************************************************/ int GMLReader::SetupParser() { if (fpGML == NULL) fpGML = VSIFOpenL(m_pszFilename, "rt"); if (fpGML != NULL) VSIFSeekL( fpGML, 0, SEEK_SET ); int bRet = -1; #ifdef HAVE_EXPAT if (bUseExpatReader) bRet = SetupParserExpat(); #endif #ifdef HAVE_XERCES if (!bUseExpatReader) bRet = SetupParserXerces(); #endif if (bRet < 0) { CPLError(CE_Failure, CPLE_AppDefined, "SetupParser(): shouldn't happen"); return FALSE; } if (!bRet) return FALSE; m_bReadStarted = FALSE; // Push an empty state. PushState( m_poRecycledState ? m_poRecycledState : new GMLReadState() ); m_poRecycledState = NULL; return TRUE; } #ifdef HAVE_XERCES /************************************************************************/ /* SetupParserXerces() */ /************************************************************************/ int GMLReader::SetupParserXerces() { { CPLMutexHolderD(&hMutex); m_nInstanceCount++; if( m_eXercesInitState == OGRGML_XERCES_UNINITIALIZED ) { try { XMLPlatformUtils::Initialize(); } catch (const XMLException& toCatch) { CPLError( CE_Warning, CPLE_AppDefined, "Exception initializing Xerces based GML reader.\n%s", tr_strdup(toCatch.getMessage()) ); m_eXercesInitState = OGRGML_XERCES_INIT_FAILED; return FALSE; } m_eXercesInitState = OGRGML_XERCES_INIT_SUCCESSFUL; } if( m_eXercesInitState != OGRGML_XERCES_INIT_SUCCESSFUL ) return FALSE; } // Cleanup any old parser. if( m_poSAXReader != NULL ) CleanupParser(); // Create and initialize parser. XMLCh* xmlUriValid = NULL; XMLCh* xmlUriNS = NULL; try{ m_poSAXReader = XMLReaderFactory::createXMLReader(); GMLXercesHandler* poXercesHandler = new GMLXercesHandler( this ); m_poGMLHandler = poXercesHandler; m_poSAXReader->setContentHandler( poXercesHandler ); m_poSAXReader->setErrorHandler( poXercesHandler ); m_poSAXReader->setLexicalHandler( poXercesHandler ); m_poSAXReader->setEntityResolver( poXercesHandler ); m_poSAXReader->setDTDHandler( poXercesHandler ); xmlUriValid = XMLString::transcode("http://xml.org/sax/features/validation"); xmlUriNS = XMLString::transcode("http://xml.org/sax/features/namespaces"); #if (OGR_GML_VALIDATION) m_poSAXReader->setFeature( xmlUriValid, true); m_poSAXReader->setFeature( xmlUriNS, true); m_poSAXReader->setFeature( XMLUni::fgSAX2CoreNameSpaces, true ); m_poSAXReader->setFeature( XMLUni::fgXercesSchema, true ); // m_poSAXReader->setDoSchema(true); // m_poSAXReader->setValidationSchemaFullChecking(true); #else m_poSAXReader->setFeature( XMLUni::fgSAX2CoreValidation, false); #if XERCES_VERSION_MAJOR >= 3 m_poSAXReader->setFeature( XMLUni::fgXercesSchema, false); #else m_poSAXReader->setFeature( XMLUni::fgSAX2CoreNameSpaces, false); #endif #endif XMLString::release( &xmlUriValid ); XMLString::release( &xmlUriNS ); } catch (...) { XMLString::release( &xmlUriValid ); XMLString::release( &xmlUriNS ); CPLError( CE_Warning, CPLE_AppDefined, "Exception initializing Xerces based GML reader.\n" ); return FALSE; } if (m_GMLInputSource == NULL && fpGML != NULL) m_GMLInputSource = new GMLInputSource(fpGML); return TRUE; } #endif /************************************************************************/ /* SetupParserExpat() */ /************************************************************************/ #ifdef HAVE_EXPAT int GMLReader::SetupParserExpat() { // Cleanup any old parser. if( oParser != NULL ) CleanupParser(); oParser = OGRCreateExpatXMLParser(); m_poGMLHandler = new GMLExpatHandler( this, oParser ); XML_SetElementHandler(oParser, GMLExpatHandler::startElementCbk, GMLExpatHandler::endElementCbk); XML_SetCharacterDataHandler(oParser, GMLExpatHandler::dataHandlerCbk); XML_SetUserData(oParser, m_poGMLHandler); if (pabyBuf == NULL) pabyBuf = (char*)VSIMalloc(PARSER_BUF_SIZE); if (pabyBuf == NULL) return FALSE; return TRUE; } #endif /************************************************************************/ /* CleanupParser() */ /************************************************************************/ void GMLReader::CleanupParser() { #ifdef HAVE_XERCES if( !bUseExpatReader && m_poSAXReader == NULL ) return; #endif #ifdef HAVE_EXPAT if ( bUseExpatReader && oParser == NULL ) return; #endif while( m_poState ) PopState(); #ifdef HAVE_XERCES delete m_poSAXReader; m_poSAXReader = NULL; delete m_GMLInputSource; m_GMLInputSource = NULL; delete m_poCompleteFeature; m_poCompleteFeature = NULL; m_bEOF = FALSE; #endif #ifdef HAVE_EXPAT if (oParser) XML_ParserFree(oParser); oParser = NULL; int i; for(i=nFeatureTabIndex;i<nFeatureTabLength;i++) delete ppoFeatureTab[i]; CPLFree(ppoFeatureTab); nFeatureTabIndex = 0; nFeatureTabLength = 0; nFeatureTabAlloc = 0; ppoFeatureTab = NULL; #endif delete m_poGMLHandler; m_poGMLHandler = NULL; m_bReadStarted = FALSE; } #ifdef HAVE_XERCES GMLBinInputStream::GMLBinInputStream(VSILFILE* fp) { this->fp = fp; emptyString = 0; } GMLBinInputStream::~ GMLBinInputStream() { } #if XERCES_VERSION_MAJOR >= 3 XMLFilePos GMLBinInputStream::curPos() const { return (XMLFilePos)VSIFTellL(fp); } XMLSize_t GMLBinInputStream::readBytes(XMLByte* const toFill, const XMLSize_t maxToRead) { return (XMLSize_t)VSIFReadL(toFill, 1, maxToRead, fp); } const XMLCh* GMLBinInputStream::getContentType() const { return &emptyString; } #else unsigned int GMLBinInputStream::curPos() const { return (unsigned int)VSIFTellL(fp); } unsigned int GMLBinInputStream::readBytes(XMLByte* const toFill, const unsigned int maxToRead) { return (unsigned int)VSIFReadL(toFill, 1, maxToRead, fp); } #endif GMLInputSource::GMLInputSource(VSILFILE* fp, MemoryManager* const manager) : InputSource(manager) { binInputStream = new GMLBinInputStream(fp); } GMLInputSource::~GMLInputSource() { } BinInputStream* GMLInputSource::makeStream() const { return binInputStream; } #endif // HAVE_XERCES /************************************************************************/ /* NextFeatureXerces() */ /************************************************************************/ #ifdef HAVE_XERCES GMLFeature *GMLReader::NextFeatureXerces() { GMLFeature *poReturn = NULL; if (m_bEOF) return NULL; try { if( !m_bReadStarted ) { if( m_poSAXReader == NULL ) SetupParser(); m_bReadStarted = TRUE; if (m_GMLInputSource == NULL) return NULL; if( !m_poSAXReader->parseFirst( *m_GMLInputSource, m_oToFill ) ) return NULL; } while( m_poCompleteFeature == NULL && !m_bStopParsing && m_poSAXReader->parseNext( m_oToFill ) ) {} if (m_poCompleteFeature == NULL) m_bEOF = TRUE; poReturn = m_poCompleteFeature; m_poCompleteFeature = NULL; } catch (const XMLException& toCatch) { char *pszErrorMessage = tr_strdup( toCatch.getMessage() ); CPLDebug( "GML", "Error during NextFeature()! Message:\n%s", pszErrorMessage ); CPLFree(pszErrorMessage); m_bStopParsing = TRUE; } catch (const SAXException& toCatch) { char *pszErrorMessage = tr_strdup( toCatch.getMessage() ); CPLError(CE_Failure, CPLE_AppDefined, "%s", pszErrorMessage); CPLFree(pszErrorMessage); m_bStopParsing = TRUE; } return poReturn; } #endif #ifdef HAVE_EXPAT GMLFeature *GMLReader::NextFeatureExpat() { if (!m_bReadStarted) { if (oParser == NULL) SetupParser(); m_bReadStarted = TRUE; } if (fpGML == NULL || m_bStopParsing) return NULL; if (nFeatureTabIndex < nFeatureTabLength) { return ppoFeatureTab[nFeatureTabIndex++]; } if (VSIFEofL(fpGML)) return NULL; nFeatureTabLength = 0; nFeatureTabIndex = 0; int nDone; do { /* Reset counter that is used to detect billion laugh attacks */ ((GMLExpatHandler*)m_poGMLHandler)->ResetDataHandlerCounter(); unsigned int nLen = (unsigned int)VSIFReadL( pabyBuf, 1, PARSER_BUF_SIZE, fpGML ); nDone = VSIFEofL(fpGML); if (XML_Parse(oParser, pabyBuf, nLen, nDone) == XML_STATUS_ERROR) { CPLError(CE_Failure, CPLE_AppDefined, "XML parsing of GML file failed : %s " "at line %d, column %d", XML_ErrorString(XML_GetErrorCode(oParser)), (int)XML_GetCurrentLineNumber(oParser), (int)XML_GetCurrentColumnNumber(oParser)); m_bStopParsing = TRUE; } if (!m_bStopParsing) m_bStopParsing = ((GMLExpatHandler*)m_poGMLHandler)->HasStoppedParsing(); } while (!nDone && !m_bStopParsing && nFeatureTabLength == 0); return (nFeatureTabLength) ? ppoFeatureTab[nFeatureTabIndex++] : NULL; } #endif GMLFeature *GMLReader::NextFeature() { #ifdef HAVE_EXPAT if (bUseExpatReader) return NextFeatureExpat(); #endif #ifdef HAVE_XERCES if (!bUseExpatReader) return NextFeatureXerces(); #endif CPLError(CE_Failure, CPLE_AppDefined, "NextFeature(): Should not happen"); return NULL; } /************************************************************************/ /* PushFeature() */ /* */ /* Create a feature based on the named element. If the */ /* corresponding feature class doesn't exist yet, then create */ /* it now. A new GMLReadState will be created for the feature, */ /* and it will be placed within that state. The state is */ /* pushed onto the readstate stack. */ /************************************************************************/ void GMLReader::PushFeature( const char *pszElement, const char *pszFID, int nClassIndex ) { int iClass; if( nClassIndex != INT_MAX ) { iClass = nClassIndex; } else { /* -------------------------------------------------------------------- */ /* Find the class of this element. */ /* -------------------------------------------------------------------- */ for( iClass = 0; iClass < m_nClassCount; iClass++ ) { if( EQUAL(pszElement,m_papoClass[iClass]->GetElementName()) ) break; } /* -------------------------------------------------------------------- */ /* Create a new feature class for this element, if there is no */ /* existing class for it. */ /* -------------------------------------------------------------------- */ if( iClass == m_nClassCount ) { CPLAssert( !m_bClassListLocked ); GMLFeatureClass *poNewClass = new GMLFeatureClass( pszElement ); AddClass( poNewClass ); } } /* -------------------------------------------------------------------- */ /* Create a feature of this feature class. Try to set the fid */ /* if available. */ /* -------------------------------------------------------------------- */ GMLFeature *poFeature = new GMLFeature( m_papoClass[iClass] ); if( pszFID != NULL ) { poFeature->SetFID( pszFID ); } /* -------------------------------------------------------------------- */ /* Create and push a new read state. */ /* -------------------------------------------------------------------- */ GMLReadState *poState; poState = m_poRecycledState ? m_poRecycledState : new GMLReadState(); m_poRecycledState = NULL; poState->m_poFeature = poFeature; PushState( poState ); } /************************************************************************/ /* IsFeatureElement() */ /* */ /* Based on context and the element name, is this element a new */ /* GML feature element? */ /************************************************************************/ int GMLReader::GetFeatureElementIndex( const char *pszElement, int nElementLength, GMLAppSchemaType eAppSchemaType ) { const char *pszLast = m_poState->GetLastComponent(); size_t nLenLast = m_poState->GetLastComponentLen(); if( eAppSchemaType == APPSCHEMA_MTKGML ) { if( m_poState->m_nPathLength != 1 ) return -1; } else if( (nLenLast >= 6 && EQUAL(pszLast+nLenLast-6,"member")) || (nLenLast >= 7 && EQUAL(pszLast+nLenLast-7,"members")) ) { /* Default feature name */ } else { if (nLenLast == 4 && strcmp(pszLast, "dane") == 0) { /* Polish TBD GML */ } /* Begin of OpenLS */ else if (nLenLast == 19 && nElementLength == 15 && strcmp(pszLast, "GeocodeResponseList") == 0 && strcmp(pszElement, "GeocodedAddress") == 0) { } else if (nLenLast == 22 && strcmp(pszLast, "DetermineRouteResponse") == 0) { /* We don't want the children of RouteInstructionsList */ /* to be a single feature. We want each RouteInstruction */ /* to be a feature */ if (strcmp(pszElement, "RouteInstructionsList") == 0) return -1; } else if (nElementLength == 16 && nLenLast == 21 && strcmp(pszElement, "RouteInstruction") == 0 && strcmp(pszLast, "RouteInstructionsList") == 0) { } /* End of OpenLS */ else if (nLenLast > 6 && strcmp(pszLast + nLenLast - 6, "_layer") == 0 && nElementLength > 8 && strcmp(pszElement + nElementLength - 8, "_feature") == 0) { /* GML answer of MapServer WMS GetFeatureInfo request */ } else { if( m_bClassListLocked ) { for( int i = 0; i < m_nClassCount; i++ ) { if( m_poState->osPath.size() + 1 + nElementLength == m_papoClass[i]->GetElementNameLen() && m_papoClass[i]->GetElementName()[m_poState->osPath.size()] == '|' && memcmp(m_poState->osPath.c_str(), m_papoClass[i]->GetElementName(), m_poState->osPath.size()) == 0 && memcmp(pszElement,m_papoClass[i]->GetElementName() + 1 + m_poState->osPath.size(), nElementLength) == 0 ) { return i; } } } return -1; } } // If the class list isn't locked, any element that is a featureMember // will do. if( !m_bClassListLocked ) return INT_MAX; // otherwise, find a class with the desired element name. for( int i = 0; i < m_nClassCount; i++ ) { if( nElementLength == (int)m_papoClass[i]->GetElementNameLen() && memcmp(pszElement,m_papoClass[i]->GetElementName(), nElementLength) == 0 ) return i; } return -1; } /************************************************************************/ /* IsCityGMLGenericAttributeElement() */ /************************************************************************/ int GMLReader::IsCityGMLGenericAttributeElement( const char *pszElement, void* attr ) { if( strcmp(pszElement, "stringAttribute") != 0 && strcmp(pszElement, "intAttribute") != 0 && strcmp(pszElement, "doubleAttribute") != 0 ) return FALSE; char* pszVal = m_poGMLHandler->GetAttributeValue(attr, "name"); if (pszVal == NULL) return FALSE; GMLFeatureClass *poClass = m_poState->m_poFeature->GetClass(); // If the schema is not yet locked, then any simple element // is potentially an attribute. if( !poClass->IsSchemaLocked() ) { CPLFree(pszVal); return TRUE; } for( int i = 0; i < poClass->GetPropertyCount(); i++ ) { if( strcmp(poClass->GetProperty(i)->GetSrcElement(),pszVal) == 0 ) { CPLFree(pszVal); return TRUE; } } CPLFree(pszVal); return FALSE; } /************************************************************************/ /* GetAttributeElementIndex() */ /************************************************************************/ int GMLReader::GetAttributeElementIndex( const char *pszElement, int nLen, const char *pszAttrKey ) { GMLFeatureClass *poClass = m_poState->m_poFeature->GetClass(); // If the schema is not yet locked, then any simple element // is potentially an attribute. if( !poClass->IsSchemaLocked() ) return INT_MAX; // Otherwise build the path to this element into a single string // and compare against known attributes. if( m_poState->m_nPathLength == 0 ) { if( pszAttrKey == NULL ) return poClass->GetPropertyIndexBySrcElement(pszElement, nLen); else { int nFullLen = nLen + 1 + strlen(pszAttrKey); osElemPath.reserve(nFullLen); osElemPath.assign(pszElement, nLen); osElemPath.append(1, '@'); osElemPath.append(pszAttrKey); return poClass->GetPropertyIndexBySrcElement(osElemPath.c_str(), nFullLen); } } else { int nFullLen = nLen + m_poState->osPath.size() + 1; if( pszAttrKey != NULL ) nFullLen += 1 + strlen(pszAttrKey); osElemPath.reserve(nFullLen); osElemPath.assign(m_poState->osPath); osElemPath.append(1, '|'); osElemPath.append(pszElement, nLen); if( pszAttrKey != NULL ) { osElemPath.append(1, '@'); osElemPath.append(pszAttrKey); } return poClass->GetPropertyIndexBySrcElement(osElemPath.c_str(), nFullLen); } } /************************************************************************/ /* PopState() */ /************************************************************************/ void GMLReader::PopState() { if( m_poState != NULL ) { #ifdef HAVE_XERCES if( !bUseExpatReader && m_poState->m_poFeature != NULL && m_poCompleteFeature == NULL ) { m_poCompleteFeature = m_poState->m_poFeature; m_poState->m_poFeature = NULL; } #endif #ifdef HAVE_EXPAT if ( bUseExpatReader && m_poState->m_poFeature != NULL ) { if (nFeatureTabLength >= nFeatureTabAlloc) { nFeatureTabAlloc = nFeatureTabLength * 4 / 3 + 16; ppoFeatureTab = (GMLFeature**) CPLRealloc(ppoFeatureTab, sizeof(GMLFeature*) * (nFeatureTabAlloc)); } ppoFeatureTab[nFeatureTabLength] = m_poState->m_poFeature; nFeatureTabLength++; m_poState->m_poFeature = NULL; } #endif GMLReadState *poParent; poParent = m_poState->m_poParentState; delete m_poRecycledState; m_poRecycledState = m_poState; m_poRecycledState->Reset(); m_poState = poParent; } } /************************************************************************/ /* PushState() */ /************************************************************************/ void GMLReader::PushState( GMLReadState *poState ) { poState->m_poParentState = m_poState; m_poState = poState; } /************************************************************************/ /* GetClass() */ /************************************************************************/ GMLFeatureClass *GMLReader::GetClass( int iClass ) const { if( iClass < 0 || iClass >= m_nClassCount ) return NULL; else return m_papoClass[iClass]; } /************************************************************************/ /* GetClass() */ /************************************************************************/ GMLFeatureClass *GMLReader::GetClass( const char *pszName ) const { for( int iClass = 0; iClass < m_nClassCount; iClass++ ) { if( EQUAL(GetClass(iClass)->GetName(),pszName) ) return GetClass(iClass); } return NULL; } /************************************************************************/ /* AddClass() */ /************************************************************************/ int GMLReader::AddClass( GMLFeatureClass *poNewClass ) { CPLAssert( GetClass( poNewClass->GetName() ) == NULL ); m_nClassCount++; m_papoClass = (GMLFeatureClass **) CPLRealloc( m_papoClass, sizeof(void*) * m_nClassCount ); m_papoClass[m_nClassCount-1] = poNewClass; if( poNewClass->HasFeatureProperties() ) m_bLookForClassAtAnyLevel = TRUE; return m_nClassCount-1; } /************************************************************************/ /* ClearClasses() */ /************************************************************************/ void GMLReader::ClearClasses() { for( int i = 0; i < m_nClassCount; i++ ) delete m_papoClass[i]; CPLFree( m_papoClass ); m_nClassCount = 0; m_papoClass = NULL; m_bLookForClassAtAnyLevel = FALSE; } /************************************************************************/ /* SetFeaturePropertyDirectly() */ /* */ /* Set the property value on the current feature, adding the */ /* property name to the GMLFeatureClass if required. */ /* The pszValue ownership is passed to this function. */ /************************************************************************/ void GMLReader::SetFeaturePropertyDirectly( const char *pszElement, char *pszValue, int iPropertyIn, GMLPropertyType eType ) { GMLFeature *poFeature = GetState()->m_poFeature; CPLAssert( poFeature != NULL ); /* -------------------------------------------------------------------- */ /* Does this property exist in the feature class? If not, add */ /* it. */ /* -------------------------------------------------------------------- */ GMLFeatureClass *poClass = poFeature->GetClass(); int iProperty; int nPropertyCount = poClass->GetPropertyCount(); if (iPropertyIn >= 0 && iPropertyIn < nPropertyCount) { iProperty = iPropertyIn; } else { for( iProperty=0; iProperty < nPropertyCount; iProperty++ ) { if( strcmp(poClass->GetProperty( iProperty )->GetSrcElement(), pszElement ) == 0 ) break; } if( iProperty == nPropertyCount ) { if( poClass->IsSchemaLocked() ) { CPLDebug("GML","Encountered property missing from class schema : %s.", pszElement); CPLFree(pszValue); return; } CPLString osFieldName; if( strchr(pszElement,'|') == NULL ) osFieldName = pszElement; else { osFieldName = strrchr(pszElement,'|') + 1; if( poClass->GetPropertyIndex(osFieldName) != -1 ) osFieldName = pszElement; } size_t nPos = osFieldName.find("@"); if( nPos != std::string::npos ) osFieldName[nPos] = '_'; // Does this conflict with an existing property name? while( poClass->GetProperty(osFieldName) != NULL ) { osFieldName += "_"; } GMLPropertyDefn *poPDefn = new GMLPropertyDefn(osFieldName,pszElement); if( EQUAL(CPLGetConfigOption( "GML_FIELDTYPES", ""), "ALWAYS_STRING") ) poPDefn->SetType( GMLPT_String ); else if( eType != GMLPT_Untyped ) poPDefn->SetType( eType ); if (poClass->AddProperty( poPDefn ) < 0) { delete poPDefn; CPLFree(pszValue); return; } } } /* -------------------------------------------------------------------- */ /* Set the property */ /* -------------------------------------------------------------------- */ poFeature->SetPropertyDirectly( iProperty, pszValue ); /* -------------------------------------------------------------------- */ /* Do we need to update the property type? */ /* -------------------------------------------------------------------- */ if( !poClass->IsSchemaLocked() ) { poClass->GetProperty(iProperty)->AnalysePropertyValue( poFeature->GetProperty(iProperty), m_bSetWidthFlag ); } } /************************************************************************/ /* LoadClasses() */ /************************************************************************/ int GMLReader::LoadClasses( const char *pszFile ) { // Add logic later to determine reasonable default schema file. if( pszFile == NULL ) return FALSE; /* -------------------------------------------------------------------- */ /* Load the raw XML file. */ /* -------------------------------------------------------------------- */ VSILFILE *fp; int nLength; char *pszWholeText; fp = VSIFOpenL( pszFile, "rb" ); if( fp == NULL ) { CPLError( CE_Failure, CPLE_OpenFailed, "Failed to open file %s.", pszFile ); return FALSE; } VSIFSeekL( fp, 0, SEEK_END ); nLength = (int) VSIFTellL( fp ); VSIFSeekL( fp, 0, SEEK_SET ); pszWholeText = (char *) VSIMalloc(nLength+1); if( pszWholeText == NULL ) { CPLError( CE_Failure, CPLE_AppDefined, "Failed to allocate %d byte buffer for %s,\n" "is this really a GMLFeatureClassList file?", nLength, pszFile ); VSIFCloseL( fp ); return FALSE; } if( VSIFReadL( pszWholeText, nLength, 1, fp ) != 1 ) { VSIFree( pszWholeText ); VSIFCloseL( fp ); CPLError( CE_Failure, CPLE_AppDefined, "Read failed on %s.", pszFile ); return FALSE; } pszWholeText[nLength] = '\0'; VSIFCloseL( fp ); if( strstr( pszWholeText, "<GMLFeatureClassList" ) == NULL ) { VSIFree( pszWholeText ); CPLError( CE_Failure, CPLE_AppDefined, "File %s does not contain a GMLFeatureClassList tree.", pszFile ); return FALSE; } /* -------------------------------------------------------------------- */ /* Convert to XML parse tree. */ /* -------------------------------------------------------------------- */ CPLXMLNode *psRoot; psRoot = CPLParseXMLString( pszWholeText ); VSIFree( pszWholeText ); // We assume parser will report errors via CPL. if( psRoot == NULL ) return FALSE; if( psRoot->eType != CXT_Element || !EQUAL(psRoot->pszValue,"GMLFeatureClassList") ) { CPLDestroyXMLNode(psRoot); CPLError( CE_Failure, CPLE_AppDefined, "File %s is not a GMLFeatureClassList document.", pszFile ); return FALSE; } const char* pszSequentialLayers = CPLGetXMLValue(psRoot, "SequentialLayers", NULL); if (pszSequentialLayers) m_bSequentialLayers = CSLTestBoolean(pszSequentialLayers); /* -------------------------------------------------------------------- */ /* Extract feature classes for all definitions found. */ /* -------------------------------------------------------------------- */ CPLXMLNode *psThis; for( psThis = psRoot->psChild; psThis != NULL; psThis = psThis->psNext ) { if( psThis->eType == CXT_Element && EQUAL(psThis->pszValue,"GMLFeatureClass") ) { GMLFeatureClass *poClass; poClass = new GMLFeatureClass(); if( !poClass->InitializeFromXML( psThis ) ) { delete poClass; CPLDestroyXMLNode( psRoot ); return FALSE; } poClass->SetSchemaLocked( TRUE ); AddClass( poClass ); } } CPLDestroyXMLNode( psRoot ); SetClassListLocked( TRUE ); return TRUE; } /************************************************************************/ /* SaveClasses() */ /************************************************************************/ int GMLReader::SaveClasses( const char *pszFile ) { // Add logic later to determine reasonable default schema file. if( pszFile == NULL ) return FALSE; /* -------------------------------------------------------------------- */ /* Create in memory schema tree. */ /* -------------------------------------------------------------------- */ CPLXMLNode *psRoot; psRoot = CPLCreateXMLNode( NULL, CXT_Element, "GMLFeatureClassList" ); if (m_bSequentialLayers != -1 && m_nClassCount > 1) { CPLCreateXMLElementAndValue( psRoot, "SequentialLayers", m_bSequentialLayers ? "true" : "false" ); } for( int iClass = 0; iClass < m_nClassCount; iClass++ ) { CPLAddXMLChild( psRoot, m_papoClass[iClass]->SerializeToXML() ); } /* -------------------------------------------------------------------- */ /* Serialize to disk. */ /* -------------------------------------------------------------------- */ VSILFILE *fp; int bSuccess = TRUE; char *pszWholeText = CPLSerializeXMLTree( psRoot ); CPLDestroyXMLNode( psRoot ); fp = VSIFOpenL( pszFile, "wb" ); if( fp == NULL ) bSuccess = FALSE; else if( VSIFWriteL( pszWholeText, strlen(pszWholeText), 1, fp ) != 1 ) bSuccess = FALSE; else VSIFCloseL( fp ); CPLFree( pszWholeText ); return bSuccess; } /************************************************************************/ /* PrescanForSchema() */ /* */ /* For now we use a pretty dumb approach of just doing a normal */ /* scan of the whole file, building up the schema information. */ /* Eventually we hope to do a more efficient scan when just */ /* looking for schema information. */ /************************************************************************/ int GMLReader::PrescanForSchema( int bGetExtents, int bAnalyzeSRSPerFeature ) { GMLFeature *poFeature; if( m_pszFilename == NULL ) return FALSE; SetClassListLocked( FALSE ); ClearClasses(); if( !SetupParser() ) return FALSE; m_bCanUseGlobalSRSName = TRUE; GMLFeatureClass *poLastClass = NULL; m_bSequentialLayers = TRUE; void* hCacheSRS = GML_BuildOGRGeometryFromList_CreateCache(); std::string osWork; while( (poFeature = NextFeature()) != NULL ) { GMLFeatureClass *poClass = poFeature->GetClass(); if (poLastClass != NULL && poClass != poLastClass && poClass->GetFeatureCount() != -1) m_bSequentialLayers = FALSE; poLastClass = poClass; if( poClass->GetFeatureCount() == -1 ) poClass->SetFeatureCount( 1 ); else poClass->SetFeatureCount( poClass->GetFeatureCount() + 1 ); const CPLXMLNode* const * papsGeometry = poFeature->GetGeometryList(); if( papsGeometry != NULL && papsGeometry[0] != NULL ) { if( poClass->GetGeometryPropertyCount() == 0 ) poClass->AddGeometryProperty( new GMLGeometryPropertyDefn( "", "", wkbUnknown ) ); } #ifdef SUPPORT_GEOMETRY if( bGetExtents ) { OGRGeometry *poGeometry = GML_BuildOGRGeometryFromList( papsGeometry, TRUE, m_bInvertAxisOrderIfLatLong, NULL, m_bConsiderEPSGAsURN, m_bGetSecondaryGeometryOption, hCacheSRS, m_bFaceHoleNegative ); if( poGeometry != NULL ) { double dfXMin, dfXMax, dfYMin, dfYMax; OGREnvelope sEnvelope; OGRwkbGeometryType eGType = (OGRwkbGeometryType) poClass->GetGeometryProperty(0)->GetType(); if( bAnalyzeSRSPerFeature ) { const char* pszSRSName = GML_ExtractSrsNameFromGeometry(papsGeometry, osWork, m_bConsiderEPSGAsURN); if (pszSRSName != NULL) m_bCanUseGlobalSRSName = FALSE; poClass->MergeSRSName(pszSRSName); } // Merge geometry type into layer. if( poClass->GetFeatureCount() == 1 && eGType == wkbUnknown ) eGType = wkbNone; poClass->GetGeometryProperty(0)->SetType( (int) OGRMergeGeometryTypes( eGType, poGeometry->getGeometryType() ) ); // merge extents. if (!poGeometry->IsEmpty()) { poGeometry->getEnvelope( &sEnvelope ); if( poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax) ) { dfXMin = MIN(dfXMin,sEnvelope.MinX); dfXMax = MAX(dfXMax,sEnvelope.MaxX); dfYMin = MIN(dfYMin,sEnvelope.MinY); dfYMax = MAX(dfYMax,sEnvelope.MaxY); } else { dfXMin = sEnvelope.MinX; dfXMax = sEnvelope.MaxX; dfYMin = sEnvelope.MinY; dfYMax = sEnvelope.MaxY; } poClass->SetExtents( dfXMin, dfXMax, dfYMin, dfYMax ); } delete poGeometry; } #endif /* def SUPPORT_GEOMETRY */ } delete poFeature; } GML_BuildOGRGeometryFromList_DestroyCache(hCacheSRS); for( int i = 0; i < m_nClassCount; i++ ) { GMLFeatureClass *poClass = m_papoClass[i]; const char* pszSRSName = poClass->GetSRSName(); if (m_bCanUseGlobalSRSName) pszSRSName = m_pszGlobalSRSName; OGRSpatialReference oSRS; if (m_bInvertAxisOrderIfLatLong && GML_IsSRSLatLongOrder(pszSRSName) && oSRS.SetFromUserInput(pszSRSName) == OGRERR_NONE) { OGR_SRSNode *poGEOGCS = oSRS.GetAttrNode( "GEOGCS" ); if( poGEOGCS != NULL ) poGEOGCS->StripNodes( "AXIS" ); OGR_SRSNode *poPROJCS = oSRS.GetAttrNode( "PROJCS" ); if (poPROJCS != NULL && oSRS.EPSGTreatsAsNorthingEasting()) poPROJCS->StripNodes( "AXIS" ); char* pszWKT = NULL; if (oSRS.exportToWkt(&pszWKT) == OGRERR_NONE) poClass->SetSRSName(pszWKT); CPLFree(pszWKT); /* So when we have computed the extent, we didn't know yet */ /* the SRS to use. Now we know it, we have to fix the extent */ /* order */ if (m_bCanUseGlobalSRSName) { double dfXMin, dfXMax, dfYMin, dfYMax; if( poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax) ) poClass->SetExtents( dfYMin, dfYMax, dfXMin, dfXMax ); } } else if( !bAnalyzeSRSPerFeature && pszSRSName != NULL && poClass->GetSRSName() == NULL && oSRS.SetFromUserInput(pszSRSName) == OGRERR_NONE ) { char* pszWKT = NULL; if (oSRS.exportToWkt(&pszWKT) == OGRERR_NONE) poClass->SetSRSName(pszWKT); CPLFree(pszWKT); } } CleanupParser(); return TRUE; } /************************************************************************/ /* ResetReading() */ /************************************************************************/ void GMLReader::ResetReading() { CleanupParser(); SetFilteredClassName(NULL); } /************************************************************************/ /* SetGlobalSRSName() */ /************************************************************************/ void GMLReader::SetGlobalSRSName( const char* pszGlobalSRSName ) { if (m_pszGlobalSRSName == NULL && pszGlobalSRSName != NULL) { const char* pszVertCS_EPSG; if( strncmp(pszGlobalSRSName, "EPSG:", 5) == 0 && (pszVertCS_EPSG = strstr(pszGlobalSRSName, ", EPSG:")) != NULL ) { m_pszGlobalSRSName = CPLStrdup(CPLSPrintf("EPSG:%d+%d", atoi(pszGlobalSRSName + 5), atoi(pszVertCS_EPSG + 7))); } else if (strncmp(pszGlobalSRSName, "EPSG:", 5) == 0 && m_bConsiderEPSGAsURN) { m_pszGlobalSRSName = CPLStrdup(CPLSPrintf("urn:ogc:def:crs:EPSG::%s", pszGlobalSRSName+5)); } else { m_pszGlobalSRSName = CPLStrdup(pszGlobalSRSName); } } } /************************************************************************/ /* SetFilteredClassName() */ /************************************************************************/ int GMLReader::SetFilteredClassName(const char* pszClassName) { CPLFree(m_pszFilteredClassName); m_pszFilteredClassName = (pszClassName) ? CPLStrdup(pszClassName) : NULL; m_nFilteredClassIndex = -1; if( m_pszFilteredClassName != NULL ) { for( int i = 0; i < m_nClassCount; i++ ) { if( strcmp(m_papoClass[i]->GetElementName(), pszClassName) == 0 ) { m_nFilteredClassIndex = i; break; } } } return TRUE; }