1 : /******************************************************************************
2 : * $Id: vfkreader.cpp 25721 2013-03-09 16:21:46Z martinl $
3 : *
4 : * Project: VFK Reader
5 : * Purpose: Implements VFKReader class.
6 : * Author: Martin Landa, landa.martin gmail.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2009-2010, 2012-2013, Martin Landa <landa.martin gmail.com>
10 : *
11 : * Permission is hereby granted, free of charge, to any person
12 : * obtaining a copy of this software and associated documentation
13 : * files (the "Software"), to deal in the Software without
14 : * restriction, including without limitation the rights to use, copy,
15 : * modify, merge, publish, distribute, sublicense, and/or sell copies
16 : * of the Software, and to permit persons to whom the Software is
17 : * furnished to do so, subject to the following conditions:
18 : *
19 : * The above copyright notice and this permission notice shall be
20 : * included in all copies or substantial portions of the Software.
21 : *
22 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 : * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 : * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 : * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
26 : * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
27 : * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
28 : * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 : * SOFTWARE.
30 : ****************************************************************************/
31 :
32 : #include "vfkreader.h"
33 : #include "vfkreaderp.h"
34 :
35 : #include "cpl_conv.h"
36 : #include "cpl_error.h"
37 : #include "cpl_string.h"
38 :
39 : #define SUPPORT_GEOMETRY
40 :
41 : #ifdef SUPPORT_GEOMETRY
42 : # include "ogr_geometry.h"
43 : #endif
44 :
45 : static char *GetDataBlockName(const char *);
46 :
47 : /*!
48 : \brief IVFKReader desctructor
49 : */
50 2 : IVFKReader::~IVFKReader()
51 : {
52 2 : }
53 :
54 : /*!
55 : \brief Create new instance of VFKReader
56 :
57 : \return pointer to VFKReader instance
58 : */
59 2 : IVFKReader *CreateVFKReader(const char *pszFilename)
60 : {
61 2 : return new VFKReaderSQLite(pszFilename);
62 : }
63 :
64 : /*!
65 : \brief VFKReader constructor
66 : */
67 2 : VFKReader::VFKReader(const char *pszFilename)
68 : {
69 2 : m_nDataBlockCount = 0;
70 2 : m_papoDataBlock = NULL;
71 2 : m_bLatin2 = TRUE; /* encoding ISO-8859-2 or WINDOWS-1250 */
72 :
73 : /* open VFK file for reading */
74 2 : CPLAssert(NULL != pszFilename);
75 2 : m_pszFilename = CPLStrdup(pszFilename);
76 2 : m_poFD = VSIFOpen(m_pszFilename, "rb");
77 2 : if (m_poFD == NULL) {
78 : CPLError(CE_Failure, CPLE_OpenFailed,
79 0 : "Failed to open file %s.", m_pszFilename);
80 : }
81 2 : }
82 :
83 : /*!
84 : \brief VFKReader destructor
85 : */
86 2 : VFKReader::~VFKReader()
87 : {
88 2 : CPLFree(m_pszFilename);
89 :
90 2 : if (m_poFD)
91 2 : VSIFClose(m_poFD);
92 :
93 : /* clear data blocks */
94 124 : for (int i = 0; i < m_nDataBlockCount; i++)
95 122 : delete m_papoDataBlock[i];
96 2 : CPLFree(m_papoDataBlock);
97 2 : }
98 :
99 273 : char *GetDataBlockName(const char *pszLine)
100 : {
101 : int n;
102 : const char *pszLineChar;
103 : char *pszBlockName;
104 :
105 273 : for (pszLineChar = pszLine + 2, n = 0; *pszLineChar != '\0' && *pszLineChar != ';'; pszLineChar++, n++)
106 : ;
107 :
108 273 : if (*pszLineChar == '\0')
109 0 : return NULL;
110 :
111 273 : pszBlockName = (char *) CPLMalloc(n + 1);
112 273 : strncpy(pszBlockName, pszLine + 2, n);
113 273 : pszBlockName[n] = '\0';
114 :
115 273 : return pszBlockName;
116 : }
117 :
118 : /*!
119 : \brief Read a line from file
120 :
121 : \param bRecode do recoding
122 :
123 : \return a NULL terminated string which should be freed with CPLFree().
124 : */
125 640 : char *VFKReader::ReadLine(bool bRecode)
126 : {
127 : const char *pszRawLine;
128 : char *pszLine;
129 :
130 640 : pszRawLine = CPLReadLine(m_poFD);
131 640 : if (pszRawLine == NULL)
132 0 : return NULL;
133 :
134 640 : if (bRecode)
135 : pszLine = CPLRecode(pszRawLine,
136 : m_bLatin2 ? "ISO-8859-2" : "WINDOWS-1250",
137 0 : CPL_ENC_UTF8);
138 : else {
139 640 : pszLine = (char *) CPLMalloc(strlen(pszRawLine) + 1);
140 640 : strcpy(pszLine, pszRawLine);
141 : }
142 :
143 640 : return pszLine;
144 : }
145 :
146 : /*!
147 : \brief Load data block definitions (&B)
148 :
149 : Call VFKReader::OpenFile() before this function.
150 :
151 : \return number of data blocks or -1 on error
152 : */
153 1 : int VFKReader::ReadDataBlocks()
154 : {
155 : char *pszLine, *pszBlockName;
156 :
157 : IVFKDataBlock *poNewDataBlock;
158 :
159 1 : CPLAssert(NULL != m_pszFilename);
160 :
161 1 : VSIFSeek(m_poFD, 0, SEEK_SET);
162 129 : while ((pszLine = ReadLine()) != NULL) {
163 128 : if (strlen(pszLine) < 2 || pszLine[0] != '&')
164 : {
165 0 : CPLFree(pszLine);
166 0 : continue;
167 : }
168 128 : if (pszLine[1] == 'B') {
169 61 : pszBlockName = GetDataBlockName(pszLine);
170 61 : if (pszBlockName == NULL) {
171 : CPLError(CE_Failure, CPLE_NotSupported,
172 0 : "Corrupted data - line\n%s\n", pszLine);
173 0 : CPLFree(pszLine);
174 0 : return -1;
175 : }
176 61 : poNewDataBlock = (IVFKDataBlock *) CreateDataBlock(pszBlockName);
177 61 : CPLFree(pszBlockName);
178 61 : poNewDataBlock->SetGeometryType();
179 61 : poNewDataBlock->SetProperties(pszLine);
180 61 : AddDataBlock(poNewDataBlock, pszLine);
181 : }
182 67 : else if (pszLine[1] == 'H') {
183 : /* header - metadata */
184 13 : AddInfo(pszLine);
185 : }
186 54 : else if (pszLine[1] == 'K' && strlen(pszLine) == 2) {
187 : /* end of file */
188 1 : CPLFree(pszLine);
189 1 : break;
190 : }
191 127 : CPLFree(pszLine);
192 : }
193 :
194 1 : return m_nDataBlockCount;
195 : }
196 :
197 :
198 : /*!
199 : \brief Load data records (&D)
200 :
201 : Call VFKReader::OpenFile() before this function.
202 :
203 : \return number of data records or -1 on error
204 : */
205 4 : int VFKReader::ReadDataRecords(IVFKDataBlock *poDataBlock)
206 : {
207 : const char *pszName;
208 : char *pszBlockName, *pszLine;
209 : long iFID;
210 : int nLength, iLine, nSkipped, nDupl, nRecords;
211 :
212 4 : CPLString pszMultiLine;
213 :
214 : VFKFeature *poNewFeature;
215 :
216 4 : poDataBlock->SetFeatureCount(0);
217 4 : pszName = poDataBlock->GetName();
218 :
219 4 : VSIFSeek(m_poFD, 0, SEEK_SET);
220 4 : iFID = 1L;
221 4 : iLine = nSkipped = nDupl = nRecords = 0;
222 516 : while ((pszLine = ReadLine()) != NULL) {
223 512 : iLine++;
224 512 : nLength = strlen(pszLine);
225 512 : if (nLength < 2)
226 0 : continue;
227 :
228 512 : if (pszLine[1] == 'D') {
229 212 : pszBlockName = GetDataBlockName(pszLine);
230 212 : if (pszBlockName && EQUAL(pszBlockName, pszName)) {
231 : /* merge lines if needed */
232 53 : if (pszLine[nLength - 2] == '\302' &&
233 0 : pszLine[nLength - 1] == '\244') {
234 :
235 : /* remove 0302 0244 (currency sign) from string */
236 0 : pszLine[nLength - 2] = '\0';
237 :
238 0 : pszMultiLine.clear();
239 0 : pszMultiLine = pszLine;
240 0 : CPLFree(pszLine);
241 :
242 0 : while ((pszLine = ReadLine()) != NULL &&
243 0 : pszLine[strlen(pszLine) - 2] == '\302' &&
244 0 : pszLine[strlen(pszLine) - 1] == '\244') {
245 : /* append line */
246 0 : pszMultiLine += pszLine;
247 : /* remove 0302 0244 (currency sign) from string */
248 0 : pszMultiLine[strlen(pszLine) - 2] = '\0';
249 :
250 0 : CPLFree(pszLine);
251 : }
252 0 : pszMultiLine += pszLine;
253 0 : CPLFree(pszLine);
254 :
255 0 : nLength = pszMultiLine.size();
256 0 : pszLine = (char *) CPLMalloc(nLength + 1);
257 0 : strncpy(pszLine, pszMultiLine.c_str(), nLength);
258 0 : pszLine[nLength] = '\0';
259 : }
260 :
261 53 : poNewFeature = new VFKFeature(poDataBlock, iFID++);
262 106 : if (poNewFeature->SetProperties(pszLine)) {
263 53 : if (AddFeature(poDataBlock, poNewFeature) != OGRERR_NONE) {
264 : CPLDebug("OGR-VFK",
265 : "%s: duplicated VFK data recored skipped (line %d).\n%s\n",
266 0 : pszBlockName, iLine, pszLine);
267 0 : nDupl++;
268 0 : iFID--;
269 : }
270 : else {
271 53 : nRecords++;
272 : }
273 53 : delete poNewFeature;
274 : }
275 : else {
276 : CPLDebug("OGR-VFK",
277 0 : "Invalid VFK data record skipped (line %d).\n%s\n", iLine, pszLine);
278 0 : nSkipped++;
279 : }
280 : }
281 212 : CPLFree(pszBlockName);
282 : }
283 300 : else if (pszLine[1] == 'K' && strlen(pszLine) == 2) {
284 : /* end of file */
285 4 : CPLFree(pszLine);
286 4 : break;
287 : }
288 508 : CPLFree(pszLine);
289 : }
290 :
291 4 : if (nSkipped > 0)
292 : CPLError(CE_Warning, CPLE_AppDefined,
293 : "%s: %d invalid VFK data records skipped",
294 0 : poDataBlock->GetName(), nSkipped);
295 4 : if (nDupl > 0)
296 : CPLError(CE_Warning, CPLE_AppDefined,
297 : "%s: %d duplicated VFK data records skipped",
298 0 : poDataBlock->GetName(), nDupl);
299 :
300 : CPLDebug("OGR_VFK", "VFKReader::ReadDataRecords(): name=%s n=%d",
301 4 : poDataBlock->GetName(), nRecords);
302 :
303 4 : return nRecords;
304 : }
305 :
306 0 : IVFKDataBlock *VFKReader::CreateDataBlock(const char *pszBlockName)
307 : {
308 0 : return (IVFKDataBlock *) new VFKDataBlock(pszBlockName, (IVFKReader *) this);
309 : }
310 :
311 : /*!
312 : \brief Add new data block
313 :
314 : \param poNewDataBlock pointer to VFKDataBlock instance
315 : \param pszDefn unused (FIXME ?)
316 : */
317 122 : void VFKReader::AddDataBlock(IVFKDataBlock *poNewDataBlock, const char *pszDefn)
318 : {
319 122 : m_nDataBlockCount++;
320 :
321 : m_papoDataBlock = (IVFKDataBlock **)
322 122 : CPLRealloc(m_papoDataBlock, sizeof (IVFKDataBlock *) * m_nDataBlockCount);
323 122 : m_papoDataBlock[m_nDataBlockCount-1] = poNewDataBlock;
324 122 : }
325 :
326 : /*!
327 : \brief Add feature
328 :
329 : \param poDataBlock pointer to VFKDataBlock instance
330 : \param poFeature pointer to VFKFeature instance
331 : */
332 0 : OGRErr VFKReader::AddFeature(IVFKDataBlock *poDataBlock, VFKFeature *poFeature)
333 : {
334 0 : poDataBlock->AddFeature(poFeature);
335 0 : return OGRERR_NONE;
336 : }
337 :
338 : /*!
339 : \brief Get data block
340 :
341 : \param i index (starting with 0)
342 :
343 : \return pointer to VFKDataBlock instance or NULL on failure
344 : */
345 4308 : IVFKDataBlock *VFKReader::GetDataBlock(int i) const
346 : {
347 4308 : if (i < 0 || i >= m_nDataBlockCount)
348 0 : return NULL;
349 :
350 4308 : return m_papoDataBlock[i];
351 : }
352 :
353 : /*!
354 : \brief Get data block
355 :
356 : \param pszName data block name
357 :
358 : \return pointer to VFKDataBlock instance or NULL on failure
359 : */
360 128 : IVFKDataBlock *VFKReader::GetDataBlock(const char *pszName) const
361 : {
362 4058 : for (int i = 0; i < m_nDataBlockCount; i++) {
363 4058 : if (EQUAL(GetDataBlock(i)->GetName(), pszName))
364 128 : return GetDataBlock(i);
365 : }
366 :
367 0 : return NULL;
368 : }
369 :
370 : /*!
371 : \brief Load geometry (loop datablocks)
372 :
373 : \return number of invalid features
374 : */
375 0 : int VFKReader::LoadGeometry()
376 : {
377 : long int nfeatures;
378 :
379 0 : nfeatures = 0;
380 0 : for (int i = 0; i < m_nDataBlockCount; i++) {
381 0 : nfeatures += m_papoDataBlock[i]->LoadGeometry();
382 : }
383 :
384 0 : CPLDebug("OGR_VFK", "VFKReader::LoadGeometry(): invalid=%ld", nfeatures);
385 :
386 0 : return nfeatures;
387 : }
388 :
389 : /*!
390 : \brief Add info
391 :
392 : \param pszLine pointer to line
393 : */
394 13 : void VFKReader::AddInfo(const char *pszLine)
395 : {
396 : int iKeyLength, iValueLength;
397 : char *pszKey, *pszValue;
398 : const char *poChar, *poKey, *poValue;
399 13 : CPLString key, value;
400 :
401 13 : poChar = poKey = pszLine + 2; /* &H */
402 13 : iKeyLength = 0;
403 103 : while (*poChar != '\0' && *poChar != ';') {
404 77 : iKeyLength++;
405 77 : poChar ++;
406 : }
407 13 : if (*poChar == '\0')
408 : return;
409 :
410 13 : pszKey = (char *) CPLMalloc(iKeyLength + 1);
411 13 : strncpy(pszKey, poKey, iKeyLength);
412 13 : pszKey[iKeyLength] = '\0';
413 :
414 13 : poValue = ++poChar; /* skip ';' */
415 13 : iValueLength = 0;
416 1177 : while (*poChar != '\0') {
417 1151 : iValueLength++;
418 1151 : poChar++;
419 : }
420 :
421 13 : pszValue = (char *) CPLMalloc(iValueLength + 1);
422 13 : strncpy(pszValue, poValue, iValueLength);
423 13 : pszValue[iValueLength] = '\0';
424 :
425 13 : poInfo[pszKey] = pszValue;
426 :
427 13 : if (EQUAL(pszKey, "CODEPAGE")) {
428 1 : if (!EQUAL(pszValue, "\"WE8ISO8859P2\""))
429 0 : m_bLatin2 = FALSE;
430 : }
431 :
432 13 : CPLFree(pszKey);
433 13 : CPLFree(pszValue);
434 : }
435 :
436 : /*!
437 : \brief Get info
438 :
439 : \param key key string
440 :
441 : \return pointer to value string or NULL if key not found
442 : */
443 0 : const char *VFKReader::GetInfo(const char *key)
444 : {
445 0 : if (poInfo.find(key) == poInfo.end())
446 0 : return NULL;
447 :
448 0 : return poInfo[key].c_str();
449 : }
|