/src/gdal/ogr/ogrsf_frmts/vfk/vfkreader.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: VFK Reader |
4 | | * Purpose: Implements VFKReader class. |
5 | | * Author: Martin Landa, landa.martin gmail.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2009-2018, Martin Landa <landa.martin gmail.com> |
9 | | * Copyright (c) 2012-2018, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include <sys/stat.h> |
15 | | |
16 | | #include "vfkreader.h" |
17 | | #include "vfkreaderp.h" |
18 | | |
19 | | #include "cpl_conv.h" |
20 | | #include "cpl_error.h" |
21 | | #include "cpl_string.h" |
22 | | |
23 | | #include "ogr_geometry.h" |
24 | | |
25 | | static char *GetDataBlockName(const char *); |
26 | | |
27 | | /*! |
28 | | \brief IVFKReader destructor |
29 | | */ |
30 | | IVFKReader::~IVFKReader() |
31 | 3.32k | { |
32 | 3.32k | } |
33 | | |
34 | | /*! |
35 | | \brief Create new instance of VFKReader |
36 | | |
37 | | \return pointer to VFKReader instance |
38 | | */ |
39 | | IVFKReader *CreateVFKReader(const GDALOpenInfo *poOpenInfo) |
40 | 3.32k | { |
41 | 3.32k | return new VFKReaderSQLite(poOpenInfo); |
42 | 3.32k | } |
43 | | |
44 | | /*! |
45 | | \brief VFKReader constructor |
46 | | */ |
47 | | VFKReader::VFKReader(const GDALOpenInfo *poOpenInfo) |
48 | 3.32k | : m_pszEncoding("ISO-8859-2"), // Encoding, supported are ISO-8859-2, |
49 | | // WINDOWS-1250 and UTF-8. |
50 | 3.32k | m_poFD(nullptr), m_pszFilename(CPLStrdup(poOpenInfo->pszFilename)), |
51 | 3.32k | m_poFStat((VSIStatBufL *)CPLCalloc(1, sizeof(VSIStatBufL))), |
52 | | // VFK is provided in two forms - stative and amendment data. |
53 | 3.32k | m_bAmendment(false), |
54 | | m_bFileField( |
55 | 3.32k | CPLFetchBool(poOpenInfo->papszOpenOptions, "FILE_FIELD", false)), |
56 | 3.32k | m_nDataBlockCount(0), m_papoDataBlock(nullptr) |
57 | 3.32k | { |
58 | | // Open VFK file for reading. |
59 | 3.32k | CPLAssert(nullptr != m_pszFilename); |
60 | | |
61 | 3.32k | if (VSIStatL(m_pszFilename, m_poFStat) != 0 || |
62 | 3.32k | !VSI_ISREG(m_poFStat->st_mode)) |
63 | 0 | { |
64 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, "%s is not a regular file.", |
65 | 0 | m_pszFilename); |
66 | 0 | } |
67 | | |
68 | 3.32k | m_poFD = VSIFOpenL(m_pszFilename, "rb"); |
69 | 3.32k | if (m_poFD == nullptr) |
70 | 0 | { |
71 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open file %s.", |
72 | 0 | m_pszFilename); |
73 | 0 | } |
74 | 3.32k | } |
75 | | |
76 | | /*! |
77 | | \brief VFKReader destructor |
78 | | */ |
79 | | VFKReader::~VFKReader() |
80 | 3.32k | { |
81 | 3.32k | CPLFree(m_pszFilename); |
82 | | |
83 | 3.32k | if (m_poFD) |
84 | 3.32k | VSIFCloseL(m_poFD); |
85 | 3.32k | CPLFree(m_poFStat); |
86 | | |
87 | | /* clear data blocks */ |
88 | 13.2k | for (int i = 0; i < m_nDataBlockCount; i++) |
89 | 9.95k | delete m_papoDataBlock[i]; |
90 | 3.32k | CPLFree(m_papoDataBlock); |
91 | 3.32k | } |
92 | | |
93 | | char *GetDataBlockName(const char *pszLine) |
94 | 383k | { |
95 | 383k | int n = 0; // Used after for. |
96 | 383k | const char *pszLineChar = pszLine + 2; |
97 | | |
98 | 4.79M | for (; *pszLineChar != '\0' && *pszLineChar != ';'; pszLineChar++, n++) |
99 | 4.41M | ; |
100 | | |
101 | 383k | if (*pszLineChar == '\0') |
102 | 36.5k | return nullptr; |
103 | | |
104 | 347k | char *pszBlockName = (char *)CPLMalloc(n + 1); |
105 | 347k | strncpy(pszBlockName, pszLine + 2, n); |
106 | 347k | pszBlockName[n] = '\0'; |
107 | | |
108 | 347k | return pszBlockName; |
109 | 383k | } |
110 | | |
111 | | /*! |
112 | | \brief Read a line from file |
113 | | |
114 | | \return a NULL terminated string which should be freed with CPLFree(). |
115 | | */ |
116 | | char *VFKReader::ReadLine() |
117 | 4.62M | { |
118 | 4.62M | int nBufLength; |
119 | 4.62M | const char *pszRawLine = |
120 | 4.62M | CPLReadLine3L(m_poFD, 100 * 1024, &nBufLength, nullptr); |
121 | 4.62M | if (pszRawLine == nullptr) |
122 | 7.68k | return nullptr; |
123 | | |
124 | 4.62M | char *pszLine = (char *)CPLMalloc(nBufLength + 1); |
125 | 4.62M | memcpy(pszLine, pszRawLine, nBufLength + 1); |
126 | | |
127 | 4.62M | const int nLineLength = static_cast<int>(strlen(pszRawLine)); |
128 | 4.62M | if (nLineLength != nBufLength) |
129 | 942k | { |
130 | | /* replace nul characters in line by spaces */ |
131 | 67.9M | for (int i = nLineLength; i < nBufLength; i++) |
132 | 66.9M | { |
133 | 66.9M | if (pszLine[i] == '\0') |
134 | 12.0M | pszLine[i] = ' '; |
135 | 66.9M | } |
136 | 942k | } |
137 | | |
138 | 4.62M | return pszLine; |
139 | 4.62M | } |
140 | | |
141 | | /*! |
142 | | \brief Load text encoding from header (&HENCODING) |
143 | | |
144 | | Called from VFKReader::ReadDataBlocks() |
145 | | */ |
146 | | void VFKReader::ReadEncoding() |
147 | 3.31k | { |
148 | 3.31k | VSIFSeekL(m_poFD, 0, SEEK_SET); |
149 | 3.31k | char *pszLine = nullptr; |
150 | 1.07M | while ((pszLine = ReadLine()) != nullptr) |
151 | 1.06M | { |
152 | 1.06M | if (strlen(pszLine) < 2 || pszLine[0] != '&') |
153 | 680k | { |
154 | 680k | CPLFree(pszLine); |
155 | 680k | continue; |
156 | 680k | } |
157 | 389k | if (pszLine[1] == 'B' || (pszLine[1] == 'K' && strlen(pszLine) == 2)) |
158 | 2.02k | { |
159 | | /* 'B' record closes the header section */ |
160 | | /* 'K' record is end of file */ |
161 | 2.02k | CPLFree(pszLine); |
162 | 2.02k | break; |
163 | 2.02k | } |
164 | 387k | if (pszLine[1] != 'H') |
165 | 175k | { |
166 | | /* (not) 'H' header */ |
167 | 175k | CPLFree(pszLine); |
168 | 175k | continue; |
169 | 175k | } |
170 | | |
171 | 211k | char *pszKey = pszLine + 2; /* &H */ |
172 | 211k | char *pszValue = pszKey; |
173 | 3.13M | while (*pszValue != '\0' && *pszValue != ';') |
174 | 2.91M | pszValue++; |
175 | 211k | if (*pszValue != ';') |
176 | 14.6k | { |
177 | | /* no value, ignoring */ |
178 | 14.6k | CPLFree(pszLine); |
179 | 14.6k | continue; |
180 | 14.6k | } |
181 | | |
182 | 196k | *pszValue = '\0'; |
183 | 196k | pszValue++; /* skip ; */ |
184 | 196k | if (*pszValue == '"') |
185 | 17.6k | { /* trim "" */ |
186 | 17.6k | pszValue++; |
187 | 17.6k | size_t nValueLen = strlen(pszValue); |
188 | 17.6k | if (nValueLen > 0) |
189 | 15.5k | pszValue[nValueLen - 1] = '\0'; |
190 | 17.6k | } |
191 | | |
192 | | /* read encoding to m_pszEncoding */ |
193 | 196k | if (EQUAL(pszKey, "CODEPAGE")) |
194 | 1.97k | { |
195 | 1.97k | if (EQUAL(pszValue, CPL_ENC_UTF8)) |
196 | 0 | m_pszEncoding = CPL_ENC_UTF8; |
197 | 1.97k | else if (!EQUAL(pszValue, "WE8ISO8859P2")) |
198 | 1.35k | m_pszEncoding = "WINDOWS-1250"; |
199 | 1.97k | } |
200 | | |
201 | 196k | CPLFree(pszLine); |
202 | 196k | } |
203 | 3.31k | } |
204 | | |
205 | | /*! |
206 | | \brief Load data block definitions (&B) |
207 | | |
208 | | Call VFKReader::OpenFile() before this function. |
209 | | |
210 | | \param bSuppressGeometry True for skipping geometry resolver (force wkbNone |
211 | | type) |
212 | | |
213 | | \return number of data blocks or -1 on error |
214 | | */ |
215 | | int VFKReader::ReadDataBlocks(bool bSuppressGeometry) |
216 | 3.31k | { |
217 | 3.31k | CPLAssert(nullptr != m_pszFilename); |
218 | | |
219 | | /* load text encoding in extra pass through header */ |
220 | 3.31k | ReadEncoding(); |
221 | | |
222 | 3.31k | VSIFSeekL(m_poFD, 0, SEEK_SET); |
223 | 3.31k | bool bInHeader = true; |
224 | 3.31k | char *pszLine = nullptr; |
225 | 1.75M | while ((pszLine = ReadLine()) != nullptr) |
226 | 1.74M | { |
227 | 1.74M | if (strlen(pszLine) < 2 || pszLine[0] != '&') |
228 | 1.11M | { |
229 | 1.11M | CPLFree(pszLine); |
230 | 1.11M | continue; |
231 | 1.11M | } |
232 | | |
233 | 636k | if (pszLine[1] == 'B') |
234 | 53.8k | { |
235 | 53.8k | if (bInHeader) |
236 | 2.02k | bInHeader = false; /* 'B' record closes the header section */ |
237 | | |
238 | 53.8k | char *pszBlockName = GetDataBlockName(pszLine); |
239 | 53.8k | if (pszBlockName == nullptr) |
240 | 216 | { |
241 | 216 | CPLError(CE_Failure, CPLE_NotSupported, |
242 | 216 | "Corrupted data - line\n%s\n", pszLine); |
243 | 216 | CPLFree(pszLine); |
244 | 216 | return -1; |
245 | 216 | } |
246 | | |
247 | | /* skip duplicated data blocks (when reading multiple files into |
248 | | * single DB) */ |
249 | 53.6k | if (!GetDataBlock(pszBlockName)) |
250 | 7.92k | { |
251 | 7.92k | IVFKDataBlock *poNewDataBlock = |
252 | 7.92k | (IVFKDataBlock *)CreateDataBlock(pszBlockName); |
253 | 7.92k | poNewDataBlock->SetGeometryType(bSuppressGeometry); |
254 | 7.92k | poNewDataBlock->SetProperties( |
255 | 7.92k | pszLine); /* TODO: check consistency on property level */ |
256 | | |
257 | 7.92k | AddDataBlock(poNewDataBlock, pszLine); |
258 | 7.92k | } |
259 | 53.6k | CPLFree(pszBlockName); |
260 | 53.6k | } |
261 | 582k | else if (pszLine[1] == 'H') |
262 | 247k | { |
263 | | /* check for amendment file */ |
264 | 247k | if (EQUAL(pszLine, "&HZMENY;1")) |
265 | 706 | { |
266 | 706 | m_bAmendment = true; |
267 | 706 | } |
268 | | |
269 | | /* header - metadata */ |
270 | 247k | AddInfo(pszLine); |
271 | 247k | } |
272 | 334k | else if (pszLine[1] == 'K' && strlen(pszLine) == 2) |
273 | 16 | { |
274 | | /* end of file */ |
275 | 16 | CPLFree(pszLine); |
276 | 16 | break; |
277 | 16 | } |
278 | 334k | else if (bInHeader && pszLine[1] == 'D') |
279 | 153k | { |
280 | | /* process 'D' records in the header section */ |
281 | 153k | AddInfo(pszLine); |
282 | 153k | } |
283 | | |
284 | 636k | CPLFree(pszLine); |
285 | 636k | } |
286 | | |
287 | 3.09k | return m_nDataBlockCount; |
288 | 3.31k | } |
289 | | |
290 | | /*! |
291 | | \brief Load data records (&D) |
292 | | |
293 | | Call VFKReader::OpenFile() before this function. |
294 | | |
295 | | \param poDataBlock limit to selected data block or NULL for all |
296 | | |
297 | | \return number of data records or -1 on error |
298 | | */ |
299 | | int64_t VFKReader::ReadDataRecords(IVFKDataBlock *poDataBlock) |
300 | 3.30k | { |
301 | 3.30k | const char *pszName = nullptr; |
302 | 3.30k | IVFKDataBlock *poDataBlockCurrent = nullptr; |
303 | | |
304 | 3.30k | if (poDataBlock) |
305 | 0 | { /* read only given data block */ |
306 | 0 | poDataBlockCurrent = poDataBlock; |
307 | 0 | if (poDataBlockCurrent->GetFeatureCount(FALSE) < 0) |
308 | 0 | poDataBlockCurrent->SetFeatureCount(0); |
309 | 0 | pszName = poDataBlockCurrent->GetName(); |
310 | 0 | } |
311 | 3.30k | else |
312 | 3.30k | { /* read all data blocks */ |
313 | 13.0k | for (int iDataBlock = 0; iDataBlock < GetDataBlockCount(); iDataBlock++) |
314 | 9.78k | { |
315 | 9.78k | poDataBlockCurrent = GetDataBlock(iDataBlock); |
316 | 9.78k | if (poDataBlockCurrent->GetFeatureCount(FALSE) < 0) |
317 | 9.04k | poDataBlockCurrent->SetFeatureCount(0); |
318 | 9.78k | } |
319 | 3.30k | poDataBlockCurrent = nullptr; |
320 | 3.30k | } |
321 | | |
322 | 3.30k | VSIFSeekL(m_poFD, 0, SEEK_SET); |
323 | | |
324 | 3.30k | int iLine = 0; |
325 | 3.30k | int nSkipped = 0; |
326 | 3.30k | int nDupl = 0; |
327 | 3.30k | int64_t nRecords = 0; |
328 | 3.30k | bool bInHeader = true; |
329 | 3.30k | CPLString osBlockNameLast; |
330 | 3.30k | char *pszLine = nullptr; |
331 | | |
332 | | /* currency sign in current encoding */ |
333 | 3.30k | const char *pszCurSign = "\244"; |
334 | 3.30k | if (EQUAL(m_pszEncoding, CPL_ENC_UTF8)) |
335 | 0 | pszCurSign = "\302\244"; |
336 | 3.30k | size_t nCurSignLen = strlen(pszCurSign); |
337 | | |
338 | 1.73M | while ((pszLine = ReadLine()) != nullptr) |
339 | 1.73M | { |
340 | 1.73M | iLine++; |
341 | 1.73M | size_t nLength = strlen(pszLine); |
342 | 1.73M | if (nLength < 2) |
343 | 530k | { |
344 | 530k | CPLFree(pszLine); |
345 | 530k | continue; |
346 | 530k | } |
347 | | |
348 | 1.20M | if (bInHeader && pszLine[1] == 'B') |
349 | 2.36k | bInHeader = false; /* 'B' record closes the header section */ |
350 | | |
351 | 1.20M | if (pszLine[1] == 'D') |
352 | 394k | { |
353 | 394k | if (bInHeader) |
354 | 64.8k | { |
355 | | /* skip 'D' records from the header section, already |
356 | | * processed as metadata */ |
357 | 64.8k | CPLFree(pszLine); |
358 | 64.8k | continue; |
359 | 64.8k | } |
360 | | |
361 | 329k | char *pszBlockName = GetDataBlockName(pszLine); |
362 | | |
363 | 329k | if (pszBlockName && (!pszName || EQUAL(pszBlockName, pszName))) |
364 | 293k | { |
365 | | /* merge lines if needed |
366 | | |
367 | | See http://en.wikipedia.org/wiki/ISO/IEC_8859 |
368 | | - \244 - general currency sign |
369 | | */ |
370 | 293k | if (EQUAL(pszLine + nLength - nCurSignLen, pszCurSign)) |
371 | 25.5k | { |
372 | | /* trim the currency sign and trailing spaces from line */ |
373 | 25.5k | nLength -= nCurSignLen; |
374 | 35.2k | while (nLength > 0 && pszLine[nLength - 1] == ' ') |
375 | 9.68k | nLength--; |
376 | 25.5k | pszLine[nLength] = '\0'; |
377 | | |
378 | 25.5k | CPLString osMultiLine(pszLine); |
379 | 25.5k | CPLFree(pszLine); |
380 | | |
381 | 69.1k | while ((pszLine = ReadLine()) != nullptr && |
382 | 69.1k | (nLength = strlen(pszLine)) >= nCurSignLen && |
383 | 61.0k | EQUAL(pszLine + nLength - nCurSignLen, pszCurSign)) |
384 | 43.6k | { |
385 | | /* trim leading spaces from continued line */ |
386 | 43.6k | char *pszLineTrim = pszLine; |
387 | 68.3k | while (*pszLineTrim == ' ') |
388 | 24.6k | pszLineTrim++; |
389 | | /* trim the currency sign and trailing spaces from line |
390 | | */ |
391 | 43.6k | nLength = strlen(pszLineTrim) - nCurSignLen; |
392 | 64.3k | while (nLength > 0 && pszLineTrim[nLength - 1] == ' ') |
393 | 20.7k | nLength--; |
394 | 43.6k | pszLineTrim[nLength] = '\0'; |
395 | | /* append a space and the trimmed line */ |
396 | 43.6k | osMultiLine += " "; |
397 | 43.6k | osMultiLine += pszLineTrim; |
398 | | |
399 | 43.6k | CPLFree(pszLine); |
400 | 43.6k | if (osMultiLine.size() > 100U * 1024U * 1024U) |
401 | 0 | { |
402 | 0 | CPLFree(pszBlockName); |
403 | 0 | return -1; |
404 | 0 | } |
405 | 43.6k | } |
406 | 25.5k | if (pszLine) |
407 | 25.5k | { |
408 | | /* trim leading spaces from continued line */ |
409 | 25.5k | char *pszLineTrim = pszLine; |
410 | 39.4k | while (*pszLineTrim == ' ') |
411 | 13.9k | pszLineTrim++; |
412 | | /* append a space and the trimmed line */ |
413 | 25.5k | osMultiLine += " "; |
414 | 25.5k | osMultiLine += pszLineTrim; |
415 | 25.5k | } |
416 | 25.5k | CPLFree(pszLine); |
417 | | |
418 | 25.5k | nLength = osMultiLine.size(); |
419 | 25.5k | if (nLength > 100U * 1024U * 1024U) |
420 | 0 | { |
421 | 0 | CPLFree(pszBlockName); |
422 | 0 | return -1; |
423 | 0 | } |
424 | 25.5k | pszLine = (char *)CPLMalloc(nLength + 1); |
425 | 25.5k | strncpy(pszLine, osMultiLine.c_str(), nLength); |
426 | 25.5k | pszLine[nLength] = '\0'; |
427 | 25.5k | } |
428 | | |
429 | 293k | if (!poDataBlock) |
430 | 293k | { /* read all data blocks */ |
431 | 293k | if (osBlockNameLast.empty() || |
432 | 136k | !EQUAL(pszBlockName, osBlockNameLast.c_str())) |
433 | 232k | { |
434 | 232k | poDataBlockCurrent = GetDataBlock(pszBlockName); |
435 | 232k | osBlockNameLast = CPLString(pszBlockName); |
436 | 232k | } |
437 | 293k | } |
438 | 293k | if (!poDataBlockCurrent) |
439 | 103k | { |
440 | 103k | CPLFree(pszBlockName); |
441 | 103k | CPLFree(pszLine); |
442 | 103k | continue; // assert ? |
443 | 103k | } |
444 | | |
445 | 190k | VFKFeature *poNewFeature = |
446 | 190k | new VFKFeature(poDataBlockCurrent, |
447 | 190k | poDataBlockCurrent->GetFeatureCount() + 1); |
448 | 190k | if (poNewFeature->SetProperties(pszLine)) |
449 | 109k | { |
450 | 109k | if (AddFeature(poDataBlockCurrent, poNewFeature) != |
451 | 109k | OGRERR_NONE) |
452 | 88.4k | { |
453 | 88.4k | CPLDebug("OGR-VFK", |
454 | 88.4k | "%s: duplicated VFK data record skipped " |
455 | 88.4k | "(line %d).\n%s\n", |
456 | 88.4k | pszBlockName, iLine, pszLine); |
457 | 88.4k | poDataBlockCurrent->SetIncRecordCount(RecordDuplicated); |
458 | 88.4k | } |
459 | 20.9k | else |
460 | 20.9k | { |
461 | 20.9k | nRecords++; |
462 | 20.9k | poDataBlockCurrent->SetIncRecordCount(RecordValid); |
463 | 20.9k | } |
464 | 109k | delete poNewFeature; |
465 | 109k | } |
466 | 80.7k | else |
467 | 80.7k | { |
468 | 80.7k | CPLDebug("OGR-VFK", |
469 | 80.7k | "Invalid VFK data record skipped (line %d).\n%s\n", |
470 | 80.7k | iLine, pszLine); |
471 | 80.7k | poDataBlockCurrent->SetIncRecordCount(RecordSkipped); |
472 | 80.7k | delete poNewFeature; |
473 | 80.7k | } |
474 | 190k | } |
475 | 226k | CPLFree(pszBlockName); |
476 | 226k | } |
477 | 810k | else if (pszLine[1] == 'K' && strlen(pszLine) == 2) |
478 | 18 | { |
479 | | /* end of file */ |
480 | 18 | CPLFree(pszLine); |
481 | 18 | break; |
482 | 18 | } |
483 | | |
484 | 1.03M | CPLFree(pszLine); |
485 | 1.03M | } |
486 | | |
487 | 13.0k | for (int iDataBlock = 0; iDataBlock < GetDataBlockCount(); iDataBlock++) |
488 | 9.78k | { |
489 | 9.78k | poDataBlockCurrent = GetDataBlock(iDataBlock); |
490 | | |
491 | 9.78k | if (poDataBlock && poDataBlock != poDataBlockCurrent) |
492 | 0 | continue; |
493 | | |
494 | 9.78k | nSkipped = poDataBlockCurrent->GetRecordCount(RecordSkipped); |
495 | 9.78k | nDupl = poDataBlockCurrent->GetRecordCount(RecordDuplicated); |
496 | 9.78k | if (nSkipped > 0) |
497 | 1.55k | CPLError(CE_Warning, CPLE_AppDefined, |
498 | 1.55k | "%s: %d invalid VFK data records skipped", |
499 | 1.55k | poDataBlockCurrent->GetName(), nSkipped); |
500 | 9.78k | if (nDupl > 0) |
501 | 1.03k | CPLError(CE_Warning, CPLE_AppDefined, |
502 | 1.03k | "%s: %d duplicated VFK data records skipped", |
503 | 1.03k | poDataBlockCurrent->GetName(), nDupl); |
504 | | |
505 | 9.78k | CPLDebug("OGR-VFK", "VFKReader::ReadDataRecords(): name=%s n=%d", |
506 | 9.78k | poDataBlockCurrent->GetName(), |
507 | 9.78k | poDataBlockCurrent->GetRecordCount(RecordValid)); |
508 | 9.78k | } |
509 | | |
510 | 3.30k | return nRecords; |
511 | 3.30k | } |
512 | | |
513 | | IVFKDataBlock *VFKReader::CreateDataBlock(const char *pszBlockName) |
514 | 0 | { |
515 | 0 | return (IVFKDataBlock *)new VFKDataBlock(pszBlockName, (IVFKReader *)this); |
516 | 0 | } |
517 | | |
518 | | /*! |
519 | | \brief Add new data block |
520 | | |
521 | | \param poNewDataBlock pointer to VFKDataBlock instance |
522 | | \param pszDefn unused (see VFKReaderSQLite::AddDataBlock) |
523 | | */ |
524 | | void VFKReader::AddDataBlock(IVFKDataBlock *poNewDataBlock, |
525 | | CPL_UNUSED const char *pszDefn) |
526 | 9.95k | { |
527 | 9.95k | m_nDataBlockCount++; |
528 | | |
529 | 9.95k | m_papoDataBlock = (IVFKDataBlock **)CPLRealloc( |
530 | 9.95k | m_papoDataBlock, sizeof(IVFKDataBlock *) * m_nDataBlockCount); |
531 | 9.95k | m_papoDataBlock[m_nDataBlockCount - 1] = poNewDataBlock; |
532 | 9.95k | } |
533 | | |
534 | | /*! |
535 | | \brief Add feature |
536 | | |
537 | | \param poDataBlock pointer to VFKDataBlock instance |
538 | | \param poFeature pointer to VFKFeature instance |
539 | | */ |
540 | | OGRErr VFKReader::AddFeature(IVFKDataBlock *poDataBlock, VFKFeature *poFeature) |
541 | 0 | { |
542 | 0 | poDataBlock->AddFeature(poFeature); |
543 | 0 | return OGRERR_NONE; |
544 | 0 | } |
545 | | |
546 | | /*! |
547 | | \brief Get data block |
548 | | |
549 | | \param i index (starting with 0) |
550 | | |
551 | | \return pointer to VFKDataBlock instance or NULL on failure |
552 | | */ |
553 | | IVFKDataBlock *VFKReader::GetDataBlock(int i) const |
554 | 1.03M | { |
555 | 1.03M | if (i < 0 || i >= m_nDataBlockCount) |
556 | 0 | return nullptr; |
557 | | |
558 | 1.03M | return m_papoDataBlock[i]; |
559 | 1.03M | } |
560 | | |
561 | | /*! |
562 | | \brief Get data block |
563 | | |
564 | | \param pszName data block name |
565 | | |
566 | | \return pointer to VFKDataBlock instance or NULL on failure |
567 | | */ |
568 | | IVFKDataBlock *VFKReader::GetDataBlock(const char *pszName) const |
569 | 297k | { |
570 | 888k | for (int i = 0; i < m_nDataBlockCount; i++) |
571 | 784k | { |
572 | 784k | if (EQUAL(GetDataBlock(i)->GetName(), pszName)) |
573 | 192k | return GetDataBlock(i); |
574 | 784k | } |
575 | | |
576 | 104k | return nullptr; |
577 | 297k | } |
578 | | |
579 | | /*! |
580 | | \brief Load geometry (loop datablocks) |
581 | | |
582 | | \return number of invalid features |
583 | | */ |
584 | | int VFKReader::LoadGeometry() |
585 | 0 | { |
586 | 0 | long int nfeatures = 0; |
587 | 0 | for (int i = 0; i < m_nDataBlockCount; i++) |
588 | 0 | { |
589 | 0 | nfeatures += m_papoDataBlock[i]->LoadGeometry(); |
590 | 0 | } |
591 | |
|
592 | 0 | CPLDebug("OGR_VFK", "VFKReader::LoadGeometry(): invalid=%ld", nfeatures); |
593 | |
|
594 | 0 | return static_cast<int>(nfeatures); |
595 | 0 | } |
596 | | |
597 | | /*! |
598 | | \brief Add info |
599 | | |
600 | | \param pszLine pointer to line |
601 | | */ |
602 | | void VFKReader::AddInfo(const char *pszLine) |
603 | 401k | { |
604 | 401k | const int nOffset = pszLine[1] == 'H' ? 2 : 1; // &DKATUZE |
605 | | |
606 | 401k | const char *poKey = pszLine + nOffset; /* &H */ |
607 | 401k | const char *poChar = poKey; |
608 | 401k | int iKeyLength = 0; |
609 | 7.04M | while (*poChar != '\0' && *poChar != ';') |
610 | 6.64M | { |
611 | 6.64M | iKeyLength++; |
612 | 6.64M | poChar++; |
613 | 6.64M | } |
614 | 401k | if (*poChar == '\0') |
615 | 43.9k | return; |
616 | | |
617 | 357k | char *pszKey = (char *)CPLMalloc(iKeyLength + 1); |
618 | 357k | strncpy(pszKey, poKey, iKeyLength); |
619 | 357k | pszKey[iKeyLength] = '\0'; |
620 | | |
621 | 357k | poChar++; /* skip ; */ |
622 | | |
623 | 357k | int iValueLength = 0; |
624 | 357k | int nSkip = 3; /* &H + ; */ |
625 | 7.52M | while (*poChar != '\0') |
626 | 7.17M | { |
627 | 7.17M | if (*poChar == '"' && iValueLength == 0) |
628 | 68.1k | { |
629 | 68.1k | nSkip++; |
630 | 68.1k | } |
631 | 7.10M | else |
632 | 7.10M | { |
633 | 7.10M | iValueLength++; |
634 | 7.10M | } |
635 | 7.17M | poChar++; |
636 | 7.17M | } |
637 | 357k | if (nSkip > 3 && iValueLength > 0) |
638 | 28.7k | iValueLength--; |
639 | | |
640 | 357k | char *pszValue = (char *)CPLMalloc(iValueLength + 1); |
641 | 7.43M | for (int i = 0; i < iValueLength; i++) |
642 | 7.07M | { |
643 | 7.07M | pszValue[i] = pszLine[iKeyLength + nSkip + i]; |
644 | 7.07M | if (pszValue[i] == '"') |
645 | 174k | { |
646 | 174k | pszValue[i] = '\''; /* " -> ' */ |
647 | 174k | } |
648 | 7.07M | } |
649 | | |
650 | 357k | pszValue[iValueLength] = '\0'; |
651 | | |
652 | | /* recode values */ |
653 | 357k | char *pszValueEnc = CPLRecode(pszValue, m_pszEncoding, CPL_ENC_UTF8); |
654 | | |
655 | 357k | if (poInfo.find(pszKey) == poInfo.end()) |
656 | 17.3k | { |
657 | 17.3k | poInfo[pszKey] = pszValueEnc; |
658 | 17.3k | } |
659 | 340k | else |
660 | 340k | { |
661 | | /* max. number of duplicated keys can be 101 */ |
662 | 340k | const size_t nLen = strlen(pszKey) + 5; |
663 | 340k | char *pszKeyUniq = (char *)CPLMalloc(nLen); |
664 | | |
665 | 340k | int nCount = 1; /* assuming at least one match */ |
666 | 340k | for (std::map<CPLString, CPLString>::iterator i = poInfo.begin(); |
667 | 407M | i != poInfo.end(); ++i) |
668 | 407M | { |
669 | 407M | size_t iFound = i->first.find("_"); |
670 | 407M | if (iFound != std::string::npos && |
671 | 401M | EQUALN(pszKey, i->first.c_str(), iFound)) |
672 | 183M | nCount += 1; |
673 | 407M | } |
674 | | |
675 | 340k | snprintf(pszKeyUniq, nLen, "%s_%d", pszKey, nCount); |
676 | 340k | poInfo[pszKeyUniq] = pszValueEnc; |
677 | 340k | CPLFree(pszKeyUniq); |
678 | 340k | } |
679 | | |
680 | 357k | CPLFree(pszKey); |
681 | 357k | CPLFree(pszValue); |
682 | 357k | CPLFree(pszValueEnc); |
683 | 357k | } |
684 | | |
685 | | /*! |
686 | | \brief Get info |
687 | | |
688 | | \param key key string |
689 | | |
690 | | \return pointer to value string or NULL if key not found |
691 | | */ |
692 | | const char *VFKReader::GetInfo(const char *key) |
693 | 0 | { |
694 | 0 | if (poInfo.find(key) == poInfo.end()) |
695 | 0 | return nullptr; |
696 | | |
697 | 0 | return poInfo[key].c_str(); |
698 | 0 | } |