EVOLUTION-MANAGER
Edit File: vfkreader.cpp
/****************************************************************************** * $Id: vfkreader.cpp 27729 2014-09-24 00:40:16Z goatbar $ * * Project: VFK Reader * Purpose: Implements VFKReader class. * Author: Martin Landa, landa.martin gmail.com * ****************************************************************************** * Copyright (c) 2009-2014, Martin Landa <landa.martin gmail.com> * Copyright (c) 2012-2013, 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 "vfkreader.h" #include "vfkreaderp.h" #include "cpl_conv.h" #include "cpl_error.h" #include "cpl_string.h" #define SUPPORT_GEOMETRY #ifdef SUPPORT_GEOMETRY # include "ogr_geometry.h" #endif static char *GetDataBlockName(const char *); /*! \brief IVFKReader desctructor */ IVFKReader::~IVFKReader() { } /*! \brief Create new instance of VFKReader \return pointer to VFKReader instance */ IVFKReader *CreateVFKReader(const char *pszFilename) { return new VFKReaderSQLite(pszFilename); } /*! \brief VFKReader constructor */ VFKReader::VFKReader(const char *pszFilename) { m_nDataBlockCount = 0; m_papoDataBlock = NULL; m_bLatin2 = TRUE; /* encoding ISO-8859-2 or WINDOWS-1250 */ /* open VFK file for reading */ CPLAssert(NULL != pszFilename); m_pszFilename = CPLStrdup(pszFilename); m_poFD = VSIFOpen(m_pszFilename, "rb"); if (m_poFD == NULL) { CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open file %s.", m_pszFilename); } } /*! \brief VFKReader destructor */ VFKReader::~VFKReader() { CPLFree(m_pszFilename); if (m_poFD) VSIFClose(m_poFD); /* clear data blocks */ for (int i = 0; i < m_nDataBlockCount; i++) delete m_papoDataBlock[i]; CPLFree(m_papoDataBlock); } char *GetDataBlockName(const char *pszLine) { int n; const char *pszLineChar; char *pszBlockName; for (pszLineChar = pszLine + 2, n = 0; *pszLineChar != '\0' && *pszLineChar != ';'; pszLineChar++, n++) ; if (*pszLineChar == '\0') return NULL; pszBlockName = (char *) CPLMalloc(n + 1); strncpy(pszBlockName, pszLine + 2, n); pszBlockName[n] = '\0'; return pszBlockName; } /*! \brief Read a line from file \param bRecode do recoding \return a NULL terminated string which should be freed with CPLFree(). */ char *VFKReader::ReadLine(bool bRecode) { const char *pszRawLine; char *pszLine; pszRawLine = CPLReadLine(m_poFD); if (pszRawLine == NULL) return NULL; if (bRecode) pszLine = CPLRecode(pszRawLine, m_bLatin2 ? "ISO-8859-2" : "WINDOWS-1250", CPL_ENC_UTF8); else { pszLine = (char *) CPLMalloc(strlen(pszRawLine) + 1); strcpy(pszLine, pszRawLine); } return pszLine; } /*! \brief Load data block definitions (&B) Call VFKReader::OpenFile() before this function. \return number of data blocks or -1 on error */ int VFKReader::ReadDataBlocks() { char *pszLine, *pszBlockName; bool bInHeader; IVFKDataBlock *poNewDataBlock; CPLAssert(NULL != m_pszFilename); VSIFSeek(m_poFD, 0, SEEK_SET); bInHeader = TRUE; while ((pszLine = ReadLine()) != NULL) { if (strlen(pszLine) < 2 || pszLine[0] != '&') { CPLFree(pszLine); continue; } if (pszLine[1] == 'B') { if (bInHeader) bInHeader = FALSE; /* 'B' record closes the header section */ pszBlockName = GetDataBlockName(pszLine); if (pszBlockName == NULL) { CPLError(CE_Failure, CPLE_NotSupported, "Corrupted data - line\n%s\n", pszLine); CPLFree(pszLine); return -1; } poNewDataBlock = (IVFKDataBlock *) CreateDataBlock(pszBlockName); CPLFree(pszBlockName); poNewDataBlock->SetGeometryType(); poNewDataBlock->SetProperties(pszLine); AddDataBlock(poNewDataBlock, pszLine); } else if (pszLine[1] == 'H') { /* header - metadata */ AddInfo(pszLine); } else if (pszLine[1] == 'K' && strlen(pszLine) == 2) { /* end of file */ CPLFree(pszLine); break; } else if (bInHeader && pszLine[1] == 'D') { /* process 'D' records in the header section */ AddInfo(pszLine); } CPLFree(pszLine); } return m_nDataBlockCount; } /*! \brief Load data records (&D) Call VFKReader::OpenFile() before this function. \param poDataBlock limit to selected data block or NULL for all \return number of data records or -1 on error */ int VFKReader::ReadDataRecords(IVFKDataBlock *poDataBlock) { const char *pszName; char *pszBlockName, *pszLine; CPLString osBlockNameLast; int nLength, iLine, nSkipped, nDupl, nRecords; int iDataBlock; bool bInHeader; IVFKDataBlock *poDataBlockCurrent; CPLString pszMultiLine; VFKFeature *poNewFeature; if (poDataBlock) { /* read only given data block */ poDataBlockCurrent = poDataBlock; poDataBlockCurrent->SetFeatureCount(0); pszName = poDataBlockCurrent->GetName(); } else { /* read all data blocks */ pszName = NULL; for (iDataBlock = 0; iDataBlock < GetDataBlockCount(); iDataBlock++) { poDataBlockCurrent = GetDataBlock(iDataBlock); poDataBlockCurrent->SetFeatureCount(0); } poDataBlockCurrent = NULL; } VSIFSeek(m_poFD, 0, SEEK_SET); iLine = nSkipped = nDupl = nRecords = 0; bInHeader = TRUE; while ((pszLine = ReadLine()) != NULL) { iLine++; nLength = strlen(pszLine); if (nLength < 2) { CPLFree(pszLine); continue; } if (bInHeader && pszLine[1] == 'B') bInHeader = FALSE; /* 'B' record closes the header section */ if (pszLine[1] == 'D') { if (bInHeader) { /* skip 'D' records from the header section, already * processed as metadata */ CPLFree(pszLine); continue; } pszBlockName = GetDataBlockName(pszLine); if (pszBlockName && (!pszName || EQUAL(pszBlockName, pszName))) { /* merge lines if needed */ if (pszLine[nLength - 2] == '\302' && pszLine[nLength - 1] == '\244') { /* remove 0302 0244 (currency sign) from string */ pszLine[nLength - 2] = '\0'; pszMultiLine.clear(); pszMultiLine = pszLine; CPLFree(pszLine); while ((pszLine = ReadLine()) != NULL && pszLine[strlen(pszLine) - 2] == '\302' && pszLine[strlen(pszLine) - 1] == '\244') { /* append line */ pszMultiLine += pszLine; /* remove 0302 0244 (currency sign) from string */ pszMultiLine[strlen(pszLine) - 2] = '\0'; CPLFree(pszLine); } pszMultiLine += pszLine; CPLFree(pszLine); nLength = pszMultiLine.size(); pszLine = (char *) CPLMalloc(nLength + 1); strncpy(pszLine, pszMultiLine.c_str(), nLength); pszLine[nLength] = '\0'; } if (!poDataBlock && pszBlockName) { /* read all data blocks */ if (osBlockNameLast.empty() || !EQUAL(pszBlockName, osBlockNameLast.c_str())) { poDataBlockCurrent = GetDataBlock(pszBlockName); osBlockNameLast = CPLString(pszBlockName); } } if (!poDataBlockCurrent) { CPLFree(pszBlockName); continue; // assert ? } poNewFeature = new VFKFeature(poDataBlockCurrent, poDataBlockCurrent->GetFeatureCount() + 1); if (poNewFeature->SetProperties(pszLine)) { if (AddFeature(poDataBlockCurrent, poNewFeature) != OGRERR_NONE) { CPLDebug("OGR-VFK", "%s: duplicated VFK data recored skipped (line %d).\n%s\n", pszBlockName, iLine, pszLine); poDataBlockCurrent->SetIncRecordCount(RecordDuplicated); } else { nRecords++; poDataBlockCurrent->SetIncRecordCount(RecordValid); } delete poNewFeature; } else { CPLDebug("OGR-VFK", "Invalid VFK data record skipped (line %d).\n%s\n", iLine, pszLine); poDataBlockCurrent->SetIncRecordCount(RecordSkipped); } } CPLFree(pszBlockName); } else if (pszLine[1] == 'K' && strlen(pszLine) == 2) { /* end of file */ CPLFree(pszLine); break; } CPLFree(pszLine); } for (iDataBlock = 0; iDataBlock < GetDataBlockCount(); iDataBlock++) { poDataBlockCurrent = GetDataBlock(iDataBlock); if (poDataBlock && poDataBlock != poDataBlockCurrent) continue; nSkipped = poDataBlockCurrent->GetRecordCount(RecordSkipped); nDupl = poDataBlockCurrent->GetRecordCount(RecordDuplicated); if (nSkipped > 0) CPLError(CE_Warning, CPLE_AppDefined, "%s: %d invalid VFK data records skipped", poDataBlockCurrent->GetName(), nSkipped); if (nDupl > 0) CPLError(CE_Warning, CPLE_AppDefined, "%s: %d duplicated VFK data records skipped", poDataBlockCurrent->GetName(), nDupl); CPLDebug("OGR-VFK", "VFKReader::ReadDataRecords(): name=%s n=%d", poDataBlockCurrent->GetName(), poDataBlockCurrent->GetRecordCount(RecordValid)); } return nRecords; } IVFKDataBlock *VFKReader::CreateDataBlock(const char *pszBlockName) { return (IVFKDataBlock *) new VFKDataBlock(pszBlockName, (IVFKReader *) this); } /*! \brief Add new data block \param poNewDataBlock pointer to VFKDataBlock instance \param pszDefn unused (FIXME ?) */ void VFKReader::AddDataBlock(IVFKDataBlock *poNewDataBlock, CPL_UNUSED const char *pszDefn) { m_nDataBlockCount++; m_papoDataBlock = (IVFKDataBlock **) CPLRealloc(m_papoDataBlock, sizeof (IVFKDataBlock *) * m_nDataBlockCount); m_papoDataBlock[m_nDataBlockCount-1] = poNewDataBlock; } /*! \brief Add feature \param poDataBlock pointer to VFKDataBlock instance \param poFeature pointer to VFKFeature instance */ OGRErr VFKReader::AddFeature(IVFKDataBlock *poDataBlock, VFKFeature *poFeature) { poDataBlock->AddFeature(poFeature); return OGRERR_NONE; } /*! \brief Get data block \param i index (starting with 0) \return pointer to VFKDataBlock instance or NULL on failure */ IVFKDataBlock *VFKReader::GetDataBlock(int i) const { if (i < 0 || i >= m_nDataBlockCount) return NULL; return m_papoDataBlock[i]; } /*! \brief Get data block \param pszName data block name \return pointer to VFKDataBlock instance or NULL on failure */ IVFKDataBlock *VFKReader::GetDataBlock(const char *pszName) const { for (int i = 0; i < m_nDataBlockCount; i++) { if (EQUAL(GetDataBlock(i)->GetName(), pszName)) return GetDataBlock(i); } return NULL; } /*! \brief Load geometry (loop datablocks) \return number of invalid features */ int VFKReader::LoadGeometry() { long int nfeatures; nfeatures = 0; for (int i = 0; i < m_nDataBlockCount; i++) { nfeatures += m_papoDataBlock[i]->LoadGeometry(); } CPLDebug("OGR_VFK", "VFKReader::LoadGeometry(): invalid=%ld", nfeatures); return nfeatures; } /*! \brief Add info \param pszLine pointer to line */ void VFKReader::AddInfo(const char *pszLine) { int i, iKeyLength, iValueLength; int nSkip, nOffset; char *pszKey, *pszValue, *pszValueEnc; const char *poChar, *poKey; CPLString key, value; if (pszLine[1] == 'H') nOffset = 2; else nOffset = 1; /* &DKATUZE */ poChar = poKey = pszLine + nOffset; /* &H */ iKeyLength = 0; while (*poChar != '\0' && *poChar != ';') { iKeyLength++; poChar ++; } if (*poChar == '\0') return; pszKey = (char *) CPLMalloc(iKeyLength + 1); strncpy(pszKey, poKey, iKeyLength); pszKey[iKeyLength] = '\0'; poChar++; /* skip ; */ iValueLength = 0; nSkip = 3; /* &H + ; */ while (*poChar != '\0') { if (*poChar == '"' && iValueLength == 0) { nSkip++; } else { iValueLength++; } poChar++; } if (nSkip > 3) iValueLength--; pszValue = (char *) CPLMalloc(iValueLength + 1); for (i = 0; i < iValueLength; i++) { pszValue[i] = pszLine[iKeyLength+nSkip+i]; if (pszValue[i] == '"') { pszValue[i] = '\''; /* " -> ' */ } } pszValue[iValueLength] = '\0'; /* recode values, assuming Latin2 */ if (EQUAL(pszKey, "CODEPAGE")) { if (!EQUAL(pszValue, "WE8ISO8859P2")) m_bLatin2 = FALSE; } pszValueEnc = CPLRecode(pszValue, m_bLatin2 ? "ISO-8859-2" : "WINDOWS-1250", CPL_ENC_UTF8); if (poInfo.find(pszKey) == poInfo.end() ) { poInfo[pszKey] = pszValueEnc; } else { int nCount; size_t iFound; char *pszKeyUniq; /* max. number of duplicated keys can be 101 */ pszKeyUniq = (char *) CPLMalloc(strlen(pszKey) + 5); nCount = 1; /* assuming at least one match */ for(std::map<CPLString, CPLString>::iterator i = poInfo.begin(); i != poInfo.end(); ++i) { iFound = i->first.find("_"); if (iFound != std::string::npos && EQUALN(pszKey, i->first.c_str(), iFound)) nCount += 1; } sprintf(pszKeyUniq, "%s_%d", pszKey, nCount); poInfo[pszKeyUniq] = pszValueEnc; } CPLFree(pszKey); CPLFree(pszValue); CPLFree(pszValueEnc); } /*! \brief Get info \param key key string \return pointer to value string or NULL if key not found */ const char *VFKReader::GetInfo(const char *key) { if (poInfo.find(key) == poInfo.end()) return NULL; return poInfo[key].c_str(); }