/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.66k | { |
32 | 3.66k | } |
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.66k | { |
41 | 3.66k | return new VFKReaderSQLite(poOpenInfo); |
42 | 3.66k | } |
43 | | |
44 | | /*! |
45 | | \brief VFKReader constructor |
46 | | */ |
47 | | VFKReader::VFKReader(const GDALOpenInfo *poOpenInfo) |
48 | 3.66k | : m_pszEncoding("ISO-8859-2"), // Encoding, supported are ISO-8859-2, |
49 | | // WINDOWS-1250 and UTF-8. |
50 | 3.66k | m_poFD(nullptr), m_pszFilename(CPLStrdup(poOpenInfo->pszFilename)), |
51 | 3.66k | m_poFStat((VSIStatBufL *)CPLCalloc(1, sizeof(VSIStatBufL))), |
52 | | // VFK is provided in two forms - stative and amendment data. |
53 | 3.66k | m_bAmendment(false), |
54 | | m_bFileField( |
55 | 3.66k | CPLFetchBool(poOpenInfo->papszOpenOptions, "FILE_FIELD", false)), |
56 | 3.66k | m_nDataBlockCount(0), m_papoDataBlock(nullptr) |
57 | 3.66k | { |
58 | | // Open VFK file for reading. |
59 | 3.66k | CPLAssert(nullptr != m_pszFilename); |
60 | | |
61 | 3.66k | if (VSIStatL(m_pszFilename, m_poFStat) != 0 || |
62 | 3.66k | !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.66k | m_poFD = VSIFOpenL(m_pszFilename, "rb"); |
69 | 3.66k | 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.66k | } |
75 | | |
76 | | /*! |
77 | | \brief VFKReader destructor |
78 | | */ |
79 | | VFKReader::~VFKReader() |
80 | 3.66k | { |
81 | 3.66k | CPLFree(m_pszFilename); |
82 | | |
83 | 3.66k | if (m_poFD) |
84 | 3.66k | VSIFCloseL(m_poFD); |
85 | 3.66k | CPLFree(m_poFStat); |
86 | | |
87 | | /* clear data blocks */ |
88 | 14.7k | for (int i = 0; i < m_nDataBlockCount; i++) |
89 | 11.0k | delete m_papoDataBlock[i]; |
90 | 3.66k | CPLFree(m_papoDataBlock); |
91 | 3.66k | } |
92 | | |
93 | | char *GetDataBlockName(const char *pszLine) |
94 | 320k | { |
95 | 320k | int n = 0; // Used after for. |
96 | 320k | const char *pszLineChar = pszLine + 2; |
97 | | |
98 | 3.88M | for (; *pszLineChar != '\0' && *pszLineChar != ';'; pszLineChar++, n++) |
99 | 3.56M | ; |
100 | | |
101 | 320k | if (*pszLineChar == '\0') |
102 | 24.3k | return nullptr; |
103 | | |
104 | 295k | char *pszBlockName = (char *)CPLMalloc(n + 1); |
105 | 295k | strncpy(pszBlockName, pszLine + 2, n); |
106 | 295k | pszBlockName[n] = '\0'; |
107 | | |
108 | 295k | return pszBlockName; |
109 | 320k | } |
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.33M | { |
118 | 4.33M | int nBufLength; |
119 | 4.33M | const char *pszRawLine = |
120 | 4.33M | CPLReadLine3L(m_poFD, 100 * 1024, &nBufLength, nullptr); |
121 | 4.33M | if (pszRawLine == nullptr) |
122 | 8.38k | return nullptr; |
123 | | |
124 | 4.32M | char *pszLine = (char *)CPLMalloc(nBufLength + 1); |
125 | 4.32M | memcpy(pszLine, pszRawLine, nBufLength + 1); |
126 | | |
127 | 4.32M | const int nLineLength = static_cast<int>(strlen(pszRawLine)); |
128 | 4.32M | if (nLineLength != nBufLength) |
129 | 847k | { |
130 | | /* replace nul characters in line by spaces */ |
131 | 58.9M | for (int i = nLineLength; i < nBufLength; i++) |
132 | 58.0M | { |
133 | 58.0M | if (pszLine[i] == '\0') |
134 | 9.78M | pszLine[i] = ' '; |
135 | 58.0M | } |
136 | 847k | } |
137 | | |
138 | 4.32M | return pszLine; |
139 | 4.33M | } |
140 | | |
141 | | /*! |
142 | | \brief Load text encoding from header (&HENCODING) |
143 | | |
144 | | Called from VFKReader::ReadDataBlocks() |
145 | | */ |
146 | | void VFKReader::ReadEncoding() |
147 | 3.65k | { |
148 | 3.65k | VSIFSeekL(m_poFD, 0, SEEK_SET); |
149 | 3.65k | char *pszLine = nullptr; |
150 | 1.05M | while ((pszLine = ReadLine()) != nullptr) |
151 | 1.05M | { |
152 | 1.05M | if (strlen(pszLine) < 2 || pszLine[0] != '&') |
153 | 645k | { |
154 | 645k | CPLFree(pszLine); |
155 | 645k | continue; |
156 | 645k | } |
157 | 412k | if (pszLine[1] == 'B' || (pszLine[1] == 'K' && strlen(pszLine) == 2)) |
158 | 2.26k | { |
159 | | /* 'B' record closes the header section */ |
160 | | /* 'K' record is end of file */ |
161 | 2.26k | CPLFree(pszLine); |
162 | 2.26k | break; |
163 | 2.26k | } |
164 | 410k | if (pszLine[1] != 'H') |
165 | 171k | { |
166 | | /* (not) 'H' header */ |
167 | 171k | CPLFree(pszLine); |
168 | 171k | continue; |
169 | 171k | } |
170 | | |
171 | 238k | char *pszKey = pszLine + 2; /* &H */ |
172 | 238k | char *pszValue = pszKey; |
173 | 3.10M | while (*pszValue != '\0' && *pszValue != ';') |
174 | 2.86M | pszValue++; |
175 | 238k | if (*pszValue != ';') |
176 | 15.4k | { |
177 | | /* no value, ignoring */ |
178 | 15.4k | CPLFree(pszLine); |
179 | 15.4k | continue; |
180 | 15.4k | } |
181 | | |
182 | 223k | *pszValue = '\0'; |
183 | 223k | pszValue++; /* skip ; */ |
184 | 223k | if (*pszValue == '"') |
185 | 22.9k | { /* trim "" */ |
186 | 22.9k | pszValue++; |
187 | 22.9k | size_t nValueLen = strlen(pszValue); |
188 | 22.9k | if (nValueLen > 0) |
189 | 17.1k | pszValue[nValueLen - 1] = '\0'; |
190 | 22.9k | } |
191 | | |
192 | | /* read encoding to m_pszEncoding */ |
193 | 223k | if (EQUAL(pszKey, "CODEPAGE")) |
194 | 2.41k | { |
195 | 2.41k | if (EQUAL(pszValue, CPL_ENC_UTF8)) |
196 | 0 | m_pszEncoding = CPL_ENC_UTF8; |
197 | 2.41k | else if (!EQUAL(pszValue, "WE8ISO8859P2")) |
198 | 1.63k | m_pszEncoding = "WINDOWS-1250"; |
199 | 2.41k | } |
200 | | |
201 | 223k | CPLFree(pszLine); |
202 | 223k | } |
203 | 3.65k | } |
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.65k | { |
217 | 3.65k | CPLAssert(nullptr != m_pszFilename); |
218 | | |
219 | | /* load text encoding in extra pass through header */ |
220 | 3.65k | ReadEncoding(); |
221 | | |
222 | 3.65k | VSIFSeekL(m_poFD, 0, SEEK_SET); |
223 | 3.65k | bool bInHeader = true; |
224 | 3.65k | char *pszLine = nullptr; |
225 | 1.60M | while ((pszLine = ReadLine()) != nullptr) |
226 | 1.60M | { |
227 | 1.60M | if (strlen(pszLine) < 2 || pszLine[0] != '&') |
228 | 987k | { |
229 | 987k | CPLFree(pszLine); |
230 | 987k | continue; |
231 | 987k | } |
232 | | |
233 | 613k | if (pszLine[1] == 'B') |
234 | 48.9k | { |
235 | 48.9k | if (bInHeader) |
236 | 2.26k | bInHeader = false; /* 'B' record closes the header section */ |
237 | | |
238 | 48.9k | char *pszBlockName = GetDataBlockName(pszLine); |
239 | 48.9k | if (pszBlockName == nullptr) |
240 | 295 | { |
241 | 295 | CPLError(CE_Failure, CPLE_NotSupported, |
242 | 295 | "Corrupted data - line\n%s\n", pszLine); |
243 | 295 | CPLFree(pszLine); |
244 | 295 | return -1; |
245 | 295 | } |
246 | | |
247 | | /* skip duplicated data blocks (when reading multiple files into |
248 | | * single DB) */ |
249 | 48.6k | if (!GetDataBlock(pszBlockName)) |
250 | 9.21k | { |
251 | 9.21k | IVFKDataBlock *poNewDataBlock = |
252 | 9.21k | (IVFKDataBlock *)CreateDataBlock(pszBlockName); |
253 | 9.21k | poNewDataBlock->SetGeometryType(bSuppressGeometry); |
254 | 9.21k | poNewDataBlock->SetProperties( |
255 | 9.21k | pszLine); /* TODO: check consistency on property level */ |
256 | | |
257 | 9.21k | AddDataBlock(poNewDataBlock, pszLine); |
258 | 9.21k | } |
259 | 48.6k | CPLFree(pszBlockName); |
260 | 48.6k | } |
261 | 564k | else if (pszLine[1] == 'H') |
262 | 272k | { |
263 | | /* check for amendment file */ |
264 | 272k | if (EQUAL(pszLine, "&HZMENY;1")) |
265 | 699 | { |
266 | 699 | m_bAmendment = true; |
267 | 699 | } |
268 | | |
269 | | /* header - metadata */ |
270 | 272k | AddInfo(pszLine); |
271 | 272k | } |
272 | 292k | else if (pszLine[1] == 'K' && strlen(pszLine) == 2) |
273 | 15 | { |
274 | | /* end of file */ |
275 | 15 | CPLFree(pszLine); |
276 | 15 | break; |
277 | 15 | } |
278 | 292k | else if (bInHeader && pszLine[1] == 'D') |
279 | 148k | { |
280 | | /* process 'D' records in the header section */ |
281 | 148k | AddInfo(pszLine); |
282 | 148k | } |
283 | | |
284 | 613k | CPLFree(pszLine); |
285 | 613k | } |
286 | | |
287 | 3.35k | return m_nDataBlockCount; |
288 | 3.65k | } |
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.62k | { |
301 | 3.62k | const char *pszName = nullptr; |
302 | 3.62k | IVFKDataBlock *poDataBlockCurrent = nullptr; |
303 | | |
304 | 3.62k | 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.62k | else |
312 | 3.62k | { /* read all data blocks */ |
313 | 14.4k | for (int iDataBlock = 0; iDataBlock < GetDataBlockCount(); iDataBlock++) |
314 | 10.8k | { |
315 | 10.8k | poDataBlockCurrent = GetDataBlock(iDataBlock); |
316 | 10.8k | if (poDataBlockCurrent->GetFeatureCount(FALSE) < 0) |
317 | 9.97k | poDataBlockCurrent->SetFeatureCount(0); |
318 | 10.8k | } |
319 | 3.62k | poDataBlockCurrent = nullptr; |
320 | 3.62k | } |
321 | | |
322 | 3.62k | VSIFSeekL(m_poFD, 0, SEEK_SET); |
323 | | |
324 | 3.62k | int iLine = 0; |
325 | 3.62k | int nSkipped = 0; |
326 | 3.62k | int nDupl = 0; |
327 | 3.62k | int64_t nRecords = 0; |
328 | 3.62k | bool bInHeader = true; |
329 | 3.62k | CPLString osBlockNameLast; |
330 | 3.62k | char *pszLine = nullptr; |
331 | | |
332 | | /* currency sign in current encoding */ |
333 | 3.62k | const char *pszCurSign = "\244"; |
334 | 3.62k | if (EQUAL(m_pszEncoding, CPL_ENC_UTF8)) |
335 | 0 | pszCurSign = "\302\244"; |
336 | 3.62k | size_t nCurSignLen = strlen(pszCurSign); |
337 | | |
338 | 1.62M | while ((pszLine = ReadLine()) != nullptr) |
339 | 1.61M | { |
340 | 1.61M | iLine++; |
341 | 1.61M | size_t nLength = strlen(pszLine); |
342 | 1.61M | if (nLength < 2) |
343 | 474k | { |
344 | 474k | CPLFree(pszLine); |
345 | 474k | continue; |
346 | 474k | } |
347 | | |
348 | 1.14M | if (bInHeader && pszLine[1] == 'B') |
349 | 2.63k | bInHeader = false; /* 'B' record closes the header section */ |
350 | | |
351 | 1.14M | if (pszLine[1] == 'D') |
352 | 352k | { |
353 | 352k | if (bInHeader) |
354 | 80.9k | { |
355 | | /* skip 'D' records from the header section, already |
356 | | * processed as metadata */ |
357 | 80.9k | CPLFree(pszLine); |
358 | 80.9k | continue; |
359 | 80.9k | } |
360 | | |
361 | 271k | char *pszBlockName = GetDataBlockName(pszLine); |
362 | | |
363 | 271k | if (pszBlockName && (!pszName || EQUAL(pszBlockName, pszName))) |
364 | 247k | { |
365 | | /* merge lines if needed |
366 | | |
367 | | See http://en.wikipedia.org/wiki/ISO/IEC_8859 |
368 | | - \244 - general currency sign |
369 | | */ |
370 | 247k | if (EQUAL(pszLine + nLength - nCurSignLen, pszCurSign)) |
371 | 18.6k | { |
372 | | /* trim the currency sign and trailing spaces from line */ |
373 | 18.6k | nLength -= nCurSignLen; |
374 | 28.4k | while (nLength > 0 && pszLine[nLength - 1] == ' ') |
375 | 9.72k | nLength--; |
376 | 18.6k | pszLine[nLength] = '\0'; |
377 | | |
378 | 18.6k | CPLString osMultiLine(pszLine); |
379 | 18.6k | CPLFree(pszLine); |
380 | | |
381 | 50.6k | while ((pszLine = ReadLine()) != nullptr && |
382 | 50.5k | (nLength = strlen(pszLine)) >= nCurSignLen && |
383 | 44.7k | EQUAL(pszLine + nLength - nCurSignLen, pszCurSign)) |
384 | 31.9k | { |
385 | | /* trim leading spaces from continued line */ |
386 | 31.9k | char *pszLineTrim = pszLine; |
387 | 53.1k | while (*pszLineTrim == ' ') |
388 | 21.2k | pszLineTrim++; |
389 | | /* trim the currency sign and trailing spaces from line |
390 | | */ |
391 | 31.9k | nLength = strlen(pszLineTrim) - nCurSignLen; |
392 | 47.6k | while (nLength > 0 && pszLineTrim[nLength - 1] == ' ') |
393 | 15.7k | nLength--; |
394 | 31.9k | pszLineTrim[nLength] = '\0'; |
395 | | /* append a space and the trimmed line */ |
396 | 31.9k | osMultiLine += " "; |
397 | 31.9k | osMultiLine += pszLineTrim; |
398 | | |
399 | 31.9k | CPLFree(pszLine); |
400 | 31.9k | if (osMultiLine.size() > 100U * 1024U * 1024U) |
401 | 0 | { |
402 | 0 | CPLFree(pszBlockName); |
403 | 0 | return -1; |
404 | 0 | } |
405 | 31.9k | } |
406 | 18.6k | if (pszLine) |
407 | 18.6k | { |
408 | | /* trim leading spaces from continued line */ |
409 | 18.6k | char *pszLineTrim = pszLine; |
410 | 32.7k | while (*pszLineTrim == ' ') |
411 | 14.0k | pszLineTrim++; |
412 | | /* append a space and the trimmed line */ |
413 | 18.6k | osMultiLine += " "; |
414 | 18.6k | osMultiLine += pszLineTrim; |
415 | 18.6k | } |
416 | 18.6k | CPLFree(pszLine); |
417 | | |
418 | 18.6k | nLength = osMultiLine.size(); |
419 | 18.6k | if (nLength > 100U * 1024U * 1024U) |
420 | 0 | { |
421 | 0 | CPLFree(pszBlockName); |
422 | 0 | return -1; |
423 | 0 | } |
424 | 18.6k | pszLine = (char *)CPLMalloc(nLength + 1); |
425 | 18.6k | strncpy(pszLine, osMultiLine.c_str(), nLength); |
426 | 18.6k | pszLine[nLength] = '\0'; |
427 | 18.6k | } |
428 | | |
429 | 247k | if (!poDataBlock) |
430 | 247k | { /* read all data blocks */ |
431 | 247k | if (osBlockNameLast.empty() || |
432 | 115k | !EQUAL(pszBlockName, osBlockNameLast.c_str())) |
433 | 190k | { |
434 | 190k | poDataBlockCurrent = GetDataBlock(pszBlockName); |
435 | 190k | osBlockNameLast = CPLString(pszBlockName); |
436 | 190k | } |
437 | 247k | } |
438 | 247k | if (!poDataBlockCurrent) |
439 | 82.5k | { |
440 | 82.5k | CPLFree(pszBlockName); |
441 | 82.5k | CPLFree(pszLine); |
442 | 82.5k | continue; // assert ? |
443 | 82.5k | } |
444 | | |
445 | 164k | VFKFeature *poNewFeature = |
446 | 164k | new VFKFeature(poDataBlockCurrent, |
447 | 164k | poDataBlockCurrent->GetFeatureCount() + 1); |
448 | 164k | if (poNewFeature->SetProperties(pszLine)) |
449 | 101k | { |
450 | 101k | if (AddFeature(poDataBlockCurrent, poNewFeature) != |
451 | 101k | OGRERR_NONE) |
452 | 77.0k | { |
453 | 77.0k | CPLDebug("OGR-VFK", |
454 | 77.0k | "%s: duplicated VFK data record skipped " |
455 | 77.0k | "(line %d).\n%s\n", |
456 | 77.0k | pszBlockName, iLine, pszLine); |
457 | 77.0k | poDataBlockCurrent->SetIncRecordCount(RecordDuplicated); |
458 | 77.0k | } |
459 | 24.2k | else |
460 | 24.2k | { |
461 | 24.2k | nRecords++; |
462 | 24.2k | poDataBlockCurrent->SetIncRecordCount(RecordValid); |
463 | 24.2k | } |
464 | 101k | delete poNewFeature; |
465 | 101k | } |
466 | 63.3k | else |
467 | 63.3k | { |
468 | 63.3k | CPLDebug("OGR-VFK", |
469 | 63.3k | "Invalid VFK data record skipped (line %d).\n%s\n", |
470 | 63.3k | iLine, pszLine); |
471 | 63.3k | poDataBlockCurrent->SetIncRecordCount(RecordSkipped); |
472 | 63.3k | delete poNewFeature; |
473 | 63.3k | } |
474 | 164k | } |
475 | 188k | CPLFree(pszBlockName); |
476 | 188k | } |
477 | 790k | else if (pszLine[1] == 'K' && strlen(pszLine) == 2) |
478 | 17 | { |
479 | | /* end of file */ |
480 | 17 | CPLFree(pszLine); |
481 | 17 | break; |
482 | 17 | } |
483 | | |
484 | 978k | CPLFree(pszLine); |
485 | 978k | } |
486 | | |
487 | 14.4k | for (int iDataBlock = 0; iDataBlock < GetDataBlockCount(); iDataBlock++) |
488 | 10.8k | { |
489 | 10.8k | poDataBlockCurrent = GetDataBlock(iDataBlock); |
490 | | |
491 | 10.8k | if (poDataBlock && poDataBlock != poDataBlockCurrent) |
492 | 0 | continue; |
493 | | |
494 | 10.8k | nSkipped = poDataBlockCurrent->GetRecordCount(RecordSkipped); |
495 | 10.8k | nDupl = poDataBlockCurrent->GetRecordCount(RecordDuplicated); |
496 | 10.8k | if (nSkipped > 0) |
497 | 1.62k | CPLError(CE_Warning, CPLE_AppDefined, |
498 | 1.62k | "%s: %d invalid VFK data records skipped", |
499 | 1.62k | poDataBlockCurrent->GetName(), nSkipped); |
500 | 10.8k | 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 | 10.8k | CPLDebug("OGR-VFK", "VFKReader::ReadDataRecords(): name=%s n=%d", |
506 | 10.8k | poDataBlockCurrent->GetName(), |
507 | 10.8k | poDataBlockCurrent->GetRecordCount(RecordValid)); |
508 | 10.8k | } |
509 | | |
510 | 3.62k | return nRecords; |
511 | 3.62k | } |
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 | 11.0k | { |
527 | 11.0k | m_nDataBlockCount++; |
528 | | |
529 | 11.0k | m_papoDataBlock = (IVFKDataBlock **)CPLRealloc( |
530 | 11.0k | m_papoDataBlock, sizeof(IVFKDataBlock *) * m_nDataBlockCount); |
531 | 11.0k | m_papoDataBlock[m_nDataBlockCount - 1] = poNewDataBlock; |
532 | 11.0k | } |
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 | 945k | { |
555 | 945k | if (i < 0 || i >= m_nDataBlockCount) |
556 | 0 | return nullptr; |
557 | | |
558 | 945k | return m_papoDataBlock[i]; |
559 | 945k | } |
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 | 250k | { |
570 | 798k | for (int i = 0; i < m_nDataBlockCount; i++) |
571 | 713k | { |
572 | 713k | if (EQUAL(GetDataBlock(i)->GetName(), pszName)) |
573 | 165k | return GetDataBlock(i); |
574 | 713k | } |
575 | | |
576 | 85.1k | return nullptr; |
577 | 250k | } |
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 | 420k | { |
604 | 420k | const int nOffset = pszLine[1] == 'H' ? 2 : 1; // &DKATUZE |
605 | | |
606 | 420k | const char *poKey = pszLine + nOffset; /* &H */ |
607 | 420k | const char *poChar = poKey; |
608 | 420k | int iKeyLength = 0; |
609 | 6.72M | while (*poChar != '\0' && *poChar != ';') |
610 | 6.30M | { |
611 | 6.30M | iKeyLength++; |
612 | 6.30M | poChar++; |
613 | 6.30M | } |
614 | 420k | if (*poChar == '\0') |
615 | 41.0k | return; |
616 | | |
617 | 379k | char *pszKey = (char *)CPLMalloc(iKeyLength + 1); |
618 | 379k | strncpy(pszKey, poKey, iKeyLength); |
619 | 379k | pszKey[iKeyLength] = '\0'; |
620 | | |
621 | 379k | poChar++; /* skip ; */ |
622 | | |
623 | 379k | int iValueLength = 0; |
624 | 379k | int nSkip = 3; /* &H + ; */ |
625 | 8.18M | while (*poChar != '\0') |
626 | 7.80M | { |
627 | 7.80M | if (*poChar == '"' && iValueLength == 0) |
628 | 84.4k | { |
629 | 84.4k | nSkip++; |
630 | 84.4k | } |
631 | 7.72M | else |
632 | 7.72M | { |
633 | 7.72M | iValueLength++; |
634 | 7.72M | } |
635 | 7.80M | poChar++; |
636 | 7.80M | } |
637 | 379k | if (nSkip > 3 && iValueLength > 0) |
638 | 29.9k | iValueLength--; |
639 | | |
640 | 379k | char *pszValue = (char *)CPLMalloc(iValueLength + 1); |
641 | 8.07M | for (int i = 0; i < iValueLength; i++) |
642 | 7.69M | { |
643 | 7.69M | pszValue[i] = pszLine[iKeyLength + nSkip + i]; |
644 | 7.69M | if (pszValue[i] == '"') |
645 | 165k | { |
646 | 165k | pszValue[i] = '\''; /* " -> ' */ |
647 | 165k | } |
648 | 7.69M | } |
649 | | |
650 | 379k | pszValue[iValueLength] = '\0'; |
651 | | |
652 | | /* recode values */ |
653 | 379k | char *pszValueEnc = CPLRecode(pszValue, m_pszEncoding, CPL_ENC_UTF8); |
654 | | |
655 | 379k | if (poInfo.find(pszKey) == poInfo.end()) |
656 | 17.4k | { |
657 | 17.4k | poInfo[pszKey] = pszValueEnc; |
658 | 17.4k | } |
659 | 362k | else |
660 | 362k | { |
661 | | /* max. number of duplicated keys can be 101 */ |
662 | 362k | const size_t nLen = strlen(pszKey) + 5; |
663 | 362k | char *pszKeyUniq = (char *)CPLMalloc(nLen); |
664 | | |
665 | 362k | int nCount = 1; /* assuming at least one match */ |
666 | 362k | for (std::map<CPLString, CPLString>::iterator i = poInfo.begin(); |
667 | 458M | i != poInfo.end(); ++i) |
668 | 458M | { |
669 | 458M | size_t iFound = i->first.find("_"); |
670 | 458M | if (iFound != std::string::npos && |
671 | 451M | EQUALN(pszKey, i->first.c_str(), iFound)) |
672 | 199M | nCount += 1; |
673 | 458M | } |
674 | | |
675 | 362k | snprintf(pszKeyUniq, nLen, "%s_%d", pszKey, nCount); |
676 | 362k | poInfo[pszKeyUniq] = pszValueEnc; |
677 | 362k | CPLFree(pszKeyUniq); |
678 | 362k | } |
679 | | |
680 | 379k | CPLFree(pszKey); |
681 | 379k | CPLFree(pszValue); |
682 | 379k | CPLFree(pszValueEnc); |
683 | 379k | } |
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 | } |