/src/gdal/ogr/ogrsf_frmts/ntf/ntffilereader.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: NTF Translator |
4 | | * Purpose: NTFFileReader class implementation. |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 1999, Frank Warmerdam |
9 | | * Copyright (c) 2013, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include <stdarg.h> |
15 | | #include "ntf.h" |
16 | | #include "cpl_conv.h" |
17 | | #include "cpl_string.h" |
18 | | #include "ogr_api.h" |
19 | | |
20 | | #include <algorithm> |
21 | | |
22 | 0 | #define DIGIT_ZERO '0' |
23 | | |
24 | | static int DefaultNTFRecordGrouper(NTFFileReader *, NTFRecord **, NTFRecord *); |
25 | | |
26 | | /************************************************************************/ |
27 | | /* NTFFileReader */ |
28 | | /************************************************************************/ |
29 | | |
30 | | NTFFileReader::NTFFileReader(OGRNTFDataSource *poDataSource) |
31 | 2 | : pszFilename(nullptr), poDS(poDataSource), fp(nullptr), nFCCount(0), |
32 | 2 | papszFCNum(nullptr), papszFCName(nullptr), nAttCount(0), |
33 | 2 | pasAttDesc(nullptr), pszTileName(nullptr), nCoordWidth(6), nZWidth(6), |
34 | 2 | nNTFLevel(0), dfXYMult(1.0), dfZMult(1.0), dfXOrigin(0), dfYOrigin(0), |
35 | 2 | dfTileXSize(0), dfTileYSize(0), dfScale(0.0), dfPaperToGround(0.0), |
36 | 2 | nStartPos(0), nPreSavedPos(0), nPostSavedPos(0), poSavedRecord(nullptr), |
37 | 2 | nSavedFeatureId(1), nBaseFeatureId(1), nFeatureCount(-1), |
38 | 2 | pszProduct(nullptr), pszPVName(nullptr), nProduct(NPC_UNKNOWN), |
39 | 2 | pfnRecordGrouper(DefaultNTFRecordGrouper), bIndexBuilt(FALSE), |
40 | 2 | bIndexNeeded(FALSE), nRasterXSize(1), nRasterYSize(1), nRasterDataType(1), |
41 | 2 | poRasterLayer(nullptr), panColumnOffset(nullptr), bCacheLines(TRUE), |
42 | 2 | nLineCacheSize(0), papoLineCache(nullptr) |
43 | 2 | { |
44 | 2 | apoCGroup[0] = nullptr; |
45 | 2 | apoCGroup[1] = nullptr; |
46 | 2 | apoCGroup[MAX_REC_GROUP] = nullptr; |
47 | 2 | memset(adfGeoTransform, 0, sizeof(adfGeoTransform)); |
48 | 2 | memset(apoTypeTranslation, 0, sizeof(apoTypeTranslation)); |
49 | 202 | for (int i = 0; i < 100; i++) |
50 | 200 | { |
51 | 200 | anIndexSize[i] = 0; |
52 | 200 | apapoRecordIndex[i] = nullptr; |
53 | 200 | } |
54 | 2 | if (poDS->GetOption("CACHE_LINES") != nullptr && |
55 | 0 | EQUAL(poDS->GetOption("CACHE_LINES"), "OFF")) |
56 | 0 | bCacheLines = FALSE; |
57 | 2 | } |
58 | | |
59 | | /************************************************************************/ |
60 | | /* ~NTFFileReader() */ |
61 | | /************************************************************************/ |
62 | | |
63 | | NTFFileReader::~NTFFileReader() |
64 | | |
65 | 2 | { |
66 | 2 | CacheClean(); |
67 | 2 | DestroyIndex(); |
68 | 2 | ClearDefs(); |
69 | 2 | CPLFree(pszFilename); |
70 | 2 | CPLFree(panColumnOffset); |
71 | 2 | } |
72 | | |
73 | | /************************************************************************/ |
74 | | /* SetBaseFID() */ |
75 | | /************************************************************************/ |
76 | | |
77 | | void NTFFileReader::SetBaseFID(long nNewBase) |
78 | | |
79 | 0 | { |
80 | 0 | CPLAssert(nSavedFeatureId == 1); |
81 | |
|
82 | 0 | nBaseFeatureId = nNewBase; |
83 | 0 | nSavedFeatureId = nBaseFeatureId; |
84 | 0 | } |
85 | | |
86 | | /************************************************************************/ |
87 | | /* ClearDefs() */ |
88 | | /* */ |
89 | | /* Clear attribute definitions and feature classes. All the */ |
90 | | /* stuff that would have to be cleaned up by Open(), and the */ |
91 | | /* destructor. */ |
92 | | /************************************************************************/ |
93 | | |
94 | | void NTFFileReader::ClearDefs() |
95 | | |
96 | 4 | { |
97 | 4 | Close(); |
98 | | |
99 | 4 | ClearCGroup(); |
100 | | |
101 | 4 | CSLDestroy(papszFCNum); |
102 | 4 | papszFCNum = nullptr; |
103 | 4 | CSLDestroy(papszFCName); |
104 | 4 | papszFCName = nullptr; |
105 | 4 | nFCCount = 0; |
106 | | |
107 | 4 | for (int i = 0; i < nAttCount; i++) |
108 | 0 | { |
109 | 0 | if (pasAttDesc[i].poCodeList != nullptr) |
110 | 0 | delete pasAttDesc[i].poCodeList; |
111 | 0 | } |
112 | | |
113 | 4 | CPLFree(pasAttDesc); |
114 | 4 | nAttCount = 0; |
115 | 4 | pasAttDesc = nullptr; |
116 | | |
117 | 4 | CPLFree(pszProduct); |
118 | 4 | pszProduct = nullptr; |
119 | | |
120 | 4 | CPLFree(pszPVName); |
121 | 4 | pszPVName = nullptr; |
122 | | |
123 | 4 | CPLFree(pszTileName); |
124 | 4 | pszTileName = nullptr; |
125 | 4 | } |
126 | | |
127 | | /************************************************************************/ |
128 | | /* Close() */ |
129 | | /* */ |
130 | | /* Close the file, but don't wipe out our knowledge about this */ |
131 | | /* file. */ |
132 | | /************************************************************************/ |
133 | | |
134 | | void NTFFileReader::Close() |
135 | | |
136 | 4 | { |
137 | 4 | if (poSavedRecord != nullptr) |
138 | 0 | delete poSavedRecord; |
139 | 4 | poSavedRecord = nullptr; |
140 | | |
141 | 4 | nPreSavedPos = nPostSavedPos = 0; |
142 | 4 | nSavedFeatureId = nBaseFeatureId; |
143 | 4 | if (fp != nullptr) |
144 | 2 | { |
145 | 2 | VSIFCloseL(fp); |
146 | 2 | fp = nullptr; |
147 | 2 | } |
148 | | |
149 | 4 | CacheClean(); |
150 | 4 | } |
151 | | |
152 | | /************************************************************************/ |
153 | | /* Open() */ |
154 | | /************************************************************************/ |
155 | | |
156 | | int NTFFileReader::Open(const char *pszFilenameIn) |
157 | | |
158 | 2 | { |
159 | 2 | if (pszFilenameIn != nullptr) |
160 | 2 | { |
161 | 2 | ClearDefs(); |
162 | | |
163 | 2 | CPLFree(pszFilename); |
164 | 2 | pszFilename = CPLStrdup(pszFilenameIn); |
165 | 2 | } |
166 | 0 | else |
167 | 0 | Close(); |
168 | | |
169 | | /* -------------------------------------------------------------------- */ |
170 | | /* Open the file. */ |
171 | | /* -------------------------------------------------------------------- */ |
172 | 2 | fp = VSIFOpenL(pszFilename, "rb"); |
173 | | |
174 | | // notdef: we should likely issue a proper CPL error message based |
175 | | // based on errno here. |
176 | 2 | if (fp == nullptr) |
177 | 0 | { |
178 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, |
179 | 0 | "Unable to open file `%s' for read access.\n", pszFilename); |
180 | 0 | return FALSE; |
181 | 0 | } |
182 | | |
183 | | /* -------------------------------------------------------------------- */ |
184 | | /* If we are just reopening an existing file we will just scan */ |
185 | | /* past the section header ... no need to reform all the definitions.*/ |
186 | | /* -------------------------------------------------------------------- */ |
187 | 2 | if (pszFilenameIn == nullptr) |
188 | 0 | { |
189 | 0 | NTFRecord *poRecord = nullptr; |
190 | |
|
191 | 0 | for (poRecord = new NTFRecord(fp); |
192 | 0 | poRecord->GetType() != NRT_VTR && poRecord->GetType() != NRT_SHR; |
193 | 0 | poRecord = new NTFRecord(fp)) |
194 | 0 | { |
195 | 0 | delete poRecord; |
196 | 0 | } |
197 | |
|
198 | 0 | delete poRecord; |
199 | |
|
200 | 0 | return TRUE; |
201 | 0 | } |
202 | | |
203 | | /* -------------------------------------------------------------------- */ |
204 | | /* Read the first record, and verify it is a proper volume header. */ |
205 | | /* -------------------------------------------------------------------- */ |
206 | 2 | NTFRecord oVHR(fp); |
207 | | |
208 | 2 | if (oVHR.GetType() != NRT_VHR) |
209 | 0 | { |
210 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
211 | 0 | "File `%s' appears to not be a UK NTF file.\n", pszFilename); |
212 | 0 | return FALSE; |
213 | 0 | } |
214 | | |
215 | 2 | nNTFLevel = atoi(oVHR.GetField(57, 57)); |
216 | 2 | if (!(nNTFLevel >= 1 && nNTFLevel <= 5)) |
217 | 0 | { |
218 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid value : nNTFLevel = %d", |
219 | 0 | nNTFLevel); |
220 | 0 | return FALSE; |
221 | 0 | } |
222 | | |
223 | | /* -------------------------------------------------------------------- */ |
224 | | /* Read records till we get the section header. */ |
225 | | /* -------------------------------------------------------------------- */ |
226 | 2 | NTFRecord *poRecord = nullptr; |
227 | | |
228 | 2 | for (poRecord = new NTFRecord(fp); |
229 | 2 | poRecord->GetType() != NRT_VTR && poRecord->GetType() != NRT_SHR; |
230 | 2 | poRecord = new NTFRecord(fp)) |
231 | 0 | { |
232 | | /* -------------------------------------------------------------------- |
233 | | */ |
234 | | /* Handle feature class name records. */ |
235 | | /* -------------------------------------------------------------------- |
236 | | */ |
237 | 0 | if (poRecord->GetType() == NRT_FCR && poRecord->GetLength() >= 37) |
238 | 0 | { |
239 | 0 | nFCCount++; |
240 | |
|
241 | 0 | papszFCNum = CSLAddString(papszFCNum, poRecord->GetField(3, 6)); |
242 | |
|
243 | 0 | CPLString osFCName; |
244 | 0 | const char *pszData = poRecord->GetData(); |
245 | | |
246 | | // CODE_COM |
247 | 0 | int iChar = 15; |
248 | 0 | for (; pszData[iChar] == ' ' && iChar > 5; iChar--) |
249 | 0 | { |
250 | 0 | } |
251 | |
|
252 | 0 | if (iChar > 6) |
253 | 0 | osFCName += poRecord->GetField(7, iChar + 1); |
254 | | |
255 | | // STCLASS |
256 | 0 | for (iChar = 35; pszData[iChar] == ' ' && iChar > 15; iChar--) |
257 | 0 | { |
258 | 0 | } |
259 | |
|
260 | 0 | if (iChar > 15) |
261 | 0 | { |
262 | 0 | if (!osFCName.empty()) |
263 | 0 | osFCName += " : "; |
264 | 0 | osFCName += poRecord->GetField(17, iChar + 1); |
265 | 0 | } |
266 | | |
267 | | // FEATDES |
268 | 0 | for (iChar = 36; pszData[iChar] != '\0' && pszData[iChar] != '\\'; |
269 | 0 | iChar++) |
270 | 0 | { |
271 | 0 | } |
272 | |
|
273 | 0 | if (iChar > 37) |
274 | 0 | { |
275 | 0 | if (!osFCName.empty()) |
276 | 0 | osFCName += " : "; |
277 | 0 | osFCName += poRecord->GetField(37, iChar); |
278 | 0 | } |
279 | |
|
280 | 0 | papszFCName = CSLAddString(papszFCName, osFCName); |
281 | 0 | } |
282 | | |
283 | | /* -------------------------------------------------------------------- |
284 | | */ |
285 | | /* Handle attribute description records. */ |
286 | | /* -------------------------------------------------------------------- |
287 | | */ |
288 | 0 | else if (poRecord->GetType() == NRT_ADR) |
289 | 0 | { |
290 | 0 | nAttCount++; |
291 | |
|
292 | 0 | pasAttDesc = static_cast<NTFAttDesc *>( |
293 | 0 | CPLRealloc(pasAttDesc, sizeof(NTFAttDesc) * nAttCount)); |
294 | 0 | memset(&pasAttDesc[nAttCount - 1], 0, sizeof(NTFAttDesc)); |
295 | |
|
296 | 0 | if (!ProcessAttDesc(poRecord, pasAttDesc + nAttCount - 1)) |
297 | 0 | nAttCount--; |
298 | 0 | } |
299 | | |
300 | | /* -------------------------------------------------------------------- |
301 | | */ |
302 | | /* Handle attribute description records. */ |
303 | | /* -------------------------------------------------------------------- |
304 | | */ |
305 | 0 | else if (poRecord->GetType() == NRT_CODELIST) |
306 | 0 | { |
307 | 0 | NTFCodeList *poCodeList = new NTFCodeList(poRecord); |
308 | 0 | NTFAttDesc *psAttDesc = GetAttDesc(poCodeList->szValType); |
309 | 0 | if (psAttDesc == nullptr) |
310 | 0 | { |
311 | 0 | CPLDebug("NTF", "Got CODELIST for %s without ATTDESC.", |
312 | 0 | poCodeList->szValType); |
313 | 0 | delete poCodeList; |
314 | 0 | } |
315 | 0 | else if (psAttDesc->poCodeList != nullptr) |
316 | 0 | { |
317 | | // Should not happen on sane files. |
318 | 0 | delete poCodeList; |
319 | 0 | } |
320 | 0 | else |
321 | 0 | { |
322 | 0 | psAttDesc->poCodeList = poCodeList; |
323 | 0 | } |
324 | 0 | } |
325 | | |
326 | | /* -------------------------------------------------------------------- |
327 | | */ |
328 | | /* Handle database header record. */ |
329 | | /* -------------------------------------------------------------------- |
330 | | */ |
331 | 0 | else if (poRecord->GetType() == NRT_DHR && pszProduct == nullptr) |
332 | 0 | { |
333 | 0 | pszProduct = CPLStrdup(poRecord->GetField(3, 22)); |
334 | 0 | for (int iChar = static_cast<int>(strlen(pszProduct)) - 1; |
335 | 0 | iChar > 0 && pszProduct[iChar] == ' '; |
336 | 0 | pszProduct[iChar--] = '\0') |
337 | 0 | { |
338 | 0 | } |
339 | |
|
340 | 0 | pszPVName = CPLStrdup(poRecord->GetField(76 + 3, 76 + 22)); |
341 | 0 | for (int iChar = static_cast<int>(strlen(pszPVName)) - 1; |
342 | 0 | iChar > 0 && pszPVName[iChar] == ' '; |
343 | 0 | pszPVName[iChar--] = '\0') |
344 | 0 | { |
345 | 0 | } |
346 | 0 | } |
347 | |
|
348 | 0 | delete poRecord; |
349 | 0 | } |
350 | | |
351 | | /* -------------------------------------------------------------------- */ |
352 | | /* Did we fall off the end without finding what we were looking */ |
353 | | /* for? */ |
354 | | /* -------------------------------------------------------------------- */ |
355 | 2 | if (poRecord->GetType() == NRT_VTR) |
356 | 2 | { |
357 | 2 | delete poRecord; |
358 | 2 | CPLError(CE_Failure, CPLE_AppDefined, |
359 | 2 | "Could not find section header record in %s.\n", pszFilename); |
360 | 2 | return FALSE; |
361 | 2 | } |
362 | | |
363 | 0 | if (pszProduct == nullptr) |
364 | 0 | { |
365 | 0 | delete poRecord; |
366 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
367 | 0 | "Could not find product type in %s.\n", pszFilename); |
368 | 0 | return FALSE; |
369 | 0 | } |
370 | | |
371 | | /* -------------------------------------------------------------------- */ |
372 | | /* Classify the product type. */ |
373 | | /* -------------------------------------------------------------------- */ |
374 | 0 | if (STARTS_WITH_CI(pszProduct, "LAND-LINE") && strlen(pszPVName) > 5 && |
375 | 0 | CPLAtof(pszPVName + 5) < 1.3) |
376 | 0 | nProduct = NPC_LANDLINE; |
377 | 0 | else if (STARTS_WITH_CI(pszProduct, "LAND-LINE")) |
378 | 0 | nProduct = NPC_LANDLINE99; |
379 | 0 | else if (EQUAL(pszProduct, "OS_LANDRANGER_CONT")) // Panorama |
380 | 0 | nProduct = NPC_LANDRANGER_CONT; |
381 | 0 | else if (EQUAL(pszProduct, "L-F_PROFILE_CON")) // Panorama |
382 | 0 | nProduct = NPC_LANDFORM_PROFILE_CONT; |
383 | 0 | else if (STARTS_WITH_CI(pszProduct, "Strategi")) |
384 | 0 | nProduct = NPC_STRATEGI; |
385 | 0 | else if (STARTS_WITH_CI(pszProduct, "Meridian_02")) |
386 | 0 | nProduct = NPC_MERIDIAN2; |
387 | 0 | else if (STARTS_WITH_CI(pszProduct, "Meridian_01")) |
388 | 0 | nProduct = NPC_MERIDIAN; |
389 | 0 | else if (EQUAL(pszProduct, NTF_BOUNDARYLINE) && |
390 | 0 | STARTS_WITH_CI(pszPVName, "A10N_FC")) |
391 | 0 | nProduct = NPC_BOUNDARYLINE; |
392 | 0 | else if (EQUAL(pszProduct, NTF_BOUNDARYLINE) && |
393 | 0 | STARTS_WITH_CI(pszPVName, "A20N_FC")) |
394 | 0 | nProduct = NPC_BL2000; |
395 | 0 | else if (STARTS_WITH_CI(pszProduct, "BaseData.GB")) |
396 | 0 | nProduct = NPC_BASEDATA; |
397 | 0 | else if (STARTS_WITH_CI(pszProduct, "OSCAR_ASSET")) |
398 | 0 | nProduct = NPC_OSCAR_ASSET; |
399 | 0 | else if (STARTS_WITH_CI(pszProduct, "OSCAR_TRAFF")) |
400 | 0 | nProduct = NPC_OSCAR_TRAFFIC; |
401 | 0 | else if (STARTS_WITH_CI(pszProduct, "OSCAR_ROUTE")) |
402 | 0 | nProduct = NPC_OSCAR_ROUTE; |
403 | 0 | else if (STARTS_WITH_CI(pszProduct, "OSCAR_NETWO")) |
404 | 0 | nProduct = NPC_OSCAR_NETWORK; |
405 | 0 | else if (STARTS_WITH_CI(pszProduct, "ADDRESS_POI")) |
406 | 0 | nProduct = NPC_ADDRESS_POINT; |
407 | 0 | else if (STARTS_WITH_CI(pszProduct, "CODE_POINT")) |
408 | 0 | { |
409 | 0 | if (GetAttDesc("RH") == nullptr) |
410 | 0 | nProduct = NPC_CODE_POINT; |
411 | 0 | else |
412 | 0 | nProduct = NPC_CODE_POINT_PLUS; |
413 | 0 | } |
414 | 0 | else if (STARTS_WITH_CI(pszProduct, "OS_LANDRANGER_DTM")) |
415 | 0 | nProduct = NPC_LANDRANGER_DTM; |
416 | 0 | else if (STARTS_WITH_CI(pszProduct, "L-F_PROFILE_DTM")) |
417 | 0 | nProduct = NPC_LANDFORM_PROFILE_DTM; |
418 | 0 | else if (STARTS_WITH_CI(pszProduct, "NEXTMap Britain DTM")) |
419 | 0 | nProduct = NPC_LANDFORM_PROFILE_DTM; // Treat as landform |
420 | |
|
421 | 0 | if (poDS->GetOption("FORCE_GENERIC") != nullptr && |
422 | 0 | !EQUAL(poDS->GetOption("FORCE_GENERIC"), "OFF")) |
423 | 0 | nProduct = NPC_UNKNOWN; |
424 | | |
425 | | // No point in caching lines if there are no polygons. |
426 | 0 | if (nProduct != NPC_BOUNDARYLINE && nProduct != NPC_BL2000) |
427 | 0 | bCacheLines = FALSE; |
428 | | |
429 | | /* -------------------------------------------------------------------- */ |
430 | | /* Handle the section header record. */ |
431 | | /* -------------------------------------------------------------------- */ |
432 | 0 | nSavedFeatureId = nBaseFeatureId; |
433 | 0 | nStartPos = VSIFTellL(fp); |
434 | |
|
435 | 0 | pszTileName = CPLStrdup(poRecord->GetField(3, 12)); // SECT_REF |
436 | 0 | size_t nTileNameLen = strlen(pszTileName); |
437 | 0 | while (nTileNameLen > 0 && pszTileName[nTileNameLen - 1] == ' ') |
438 | 0 | { |
439 | 0 | pszTileName[nTileNameLen - 1] = '\0'; |
440 | 0 | nTileNameLen--; |
441 | 0 | } |
442 | |
|
443 | 0 | nCoordWidth = atoi(poRecord->GetField(15, 19)); // XYLEN |
444 | 0 | if (nCoordWidth <= 0) |
445 | 0 | nCoordWidth = 10; |
446 | |
|
447 | 0 | nZWidth = atoi(poRecord->GetField(31, 35)); // ZLEN |
448 | 0 | if (nZWidth <= 0) |
449 | 0 | nZWidth = 10; |
450 | |
|
451 | 0 | dfXYMult = atoi(poRecord->GetField(21, 30)) / 1000.0; // XY_MULT |
452 | 0 | dfXOrigin = atoi(poRecord->GetField(47, 56)); |
453 | 0 | dfYOrigin = atoi(poRecord->GetField(57, 66)); |
454 | 0 | dfTileXSize = atoi(poRecord->GetField(23 + 74, 32 + 74)); |
455 | 0 | dfTileYSize = atoi(poRecord->GetField(33 + 74, 42 + 74)); |
456 | 0 | dfZMult = atoi(poRecord->GetField(37, 46)) / 1000.0; |
457 | | |
458 | | /* -------------------------------------------------------------------- */ |
459 | | /* Setup scale and transformation factor for text height. */ |
460 | | /* -------------------------------------------------------------------- */ |
461 | 0 | if (poRecord->GetLength() >= 187) |
462 | 0 | dfScale = atoi(poRecord->GetField(148 + 31, 148 + 39)); |
463 | 0 | else if (nProduct == NPC_STRATEGI) |
464 | 0 | dfScale = 250000; |
465 | 0 | else if (nProduct == NPC_MERIDIAN || nProduct == NPC_MERIDIAN2) |
466 | 0 | dfScale = 100000; |
467 | 0 | else if (nProduct == NPC_LANDFORM_PROFILE_CONT) |
468 | 0 | dfScale = 10000; |
469 | 0 | else if (nProduct == NPC_LANDRANGER_CONT) |
470 | 0 | dfScale = 50000; |
471 | 0 | else if (nProduct == NPC_OSCAR_ASSET || nProduct == NPC_OSCAR_TRAFFIC || |
472 | 0 | nProduct == NPC_OSCAR_NETWORK || nProduct == NPC_OSCAR_ROUTE) |
473 | 0 | dfScale = 10000; |
474 | 0 | else if (nProduct == NPC_BASEDATA) |
475 | 0 | dfScale = 625000; |
476 | 0 | else /*if( nProduct == NPC_BOUNDARYLINE ) or default case */ |
477 | 0 | dfScale = 10000; |
478 | |
|
479 | 0 | if (dfScale != 0.0) |
480 | 0 | dfPaperToGround = dfScale / 1000.0; |
481 | 0 | else |
482 | 0 | dfPaperToGround = 0.0; |
483 | |
|
484 | 0 | delete poRecord; |
485 | | |
486 | | /* -------------------------------------------------------------------- */ |
487 | | /* Ensure we have appropriate layers defined. */ |
488 | | /* -------------------------------------------------------------------- */ |
489 | 0 | CPLErrorReset(); |
490 | |
|
491 | 0 | if (!IsRasterProduct()) |
492 | 0 | EstablishLayers(); |
493 | 0 | else |
494 | 0 | EstablishRasterAccess(); |
495 | |
|
496 | 0 | return CPLGetLastErrorType() != CE_Failure; |
497 | 0 | } |
498 | | |
499 | | /************************************************************************/ |
500 | | /* DumpReadable() */ |
501 | | /************************************************************************/ |
502 | | |
503 | | void NTFFileReader::DumpReadable(FILE *fpLog) |
504 | | |
505 | 0 | { |
506 | 0 | fprintf(fpLog, "Tile Name = %s\n", pszTileName); |
507 | 0 | fprintf(fpLog, "Product = %s\n", pszProduct); |
508 | 0 | fprintf(fpLog, "NTFLevel = %d\n", nNTFLevel); |
509 | 0 | fprintf(fpLog, "XYLEN = %d\n", nCoordWidth); |
510 | 0 | fprintf(fpLog, "XY_MULT = %g\n", dfXYMult); |
511 | 0 | fprintf(fpLog, "X_ORIG = %g\n", dfXOrigin); |
512 | 0 | fprintf(fpLog, "Y_ORIG = %g\n", dfYOrigin); |
513 | 0 | fprintf(fpLog, "XMAX = %g\n", dfTileXSize); |
514 | 0 | fprintf(fpLog, "YMAX = %g\n", dfTileYSize); |
515 | 0 | } |
516 | | |
517 | | /************************************************************************/ |
518 | | /* ProcessGeometry() */ |
519 | | /* */ |
520 | | /* Drop duplicate vertices from line strings ... they mess up */ |
521 | | /* FME's polygon handling sometimes. */ |
522 | | /************************************************************************/ |
523 | | |
524 | | OGRGeometry *NTFFileReader::ProcessGeometry(NTFRecord *poRecord, int *pnGeomId) |
525 | | |
526 | 0 | { |
527 | 0 | if (poRecord->GetType() == NRT_GEOMETRY3D) |
528 | 0 | return ProcessGeometry3D(poRecord, pnGeomId); |
529 | | |
530 | 0 | else if (poRecord->GetType() != NRT_GEOMETRY) |
531 | 0 | return nullptr; |
532 | | |
533 | 0 | const int nGType = atoi(poRecord->GetField(9, 9)); // GTYPE |
534 | 0 | const int nNumCoord = atoi(poRecord->GetField(10, 13)); // NUM_COORD |
535 | 0 | if (nNumCoord < 0) |
536 | 0 | return nullptr; |
537 | 0 | if (pnGeomId != nullptr) |
538 | 0 | *pnGeomId = atoi(poRecord->GetField(3, 8)); // GEOM_ID |
539 | | |
540 | | /* -------------------------------------------------------------------- */ |
541 | | /* Point */ |
542 | | /* -------------------------------------------------------------------- */ |
543 | 0 | OGRGeometry *poGeometry = nullptr; |
544 | 0 | if (nGType == 1) |
545 | 0 | { |
546 | 0 | const double dfX = |
547 | 0 | atoi(poRecord->GetField(14, 14 + GetXYLen() - 1)) * GetXYMult() + |
548 | 0 | GetXOrigin(); |
549 | 0 | const double dfY = |
550 | 0 | atoi(poRecord->GetField(14 + GetXYLen(), 14 + GetXYLen() * 2 - 1)) * |
551 | 0 | GetXYMult() + |
552 | 0 | GetYOrigin(); |
553 | |
|
554 | 0 | poGeometry = new OGRPoint(dfX, dfY); |
555 | 0 | } |
556 | | |
557 | | /* -------------------------------------------------------------------- */ |
558 | | /* Line (or arc) */ |
559 | | /* -------------------------------------------------------------------- */ |
560 | 0 | else if (nGType == 2 || nGType == 3 || nGType == 4) |
561 | 0 | { |
562 | |
|
563 | 0 | if (nNumCoord > 0 && poRecord->GetLength() < |
564 | 0 | 14 + (nNumCoord - 1) * (GetXYLen() * 2 + 1) + |
565 | 0 | GetXYLen() * 2 - 1) |
566 | 0 | { |
567 | 0 | return nullptr; |
568 | 0 | } |
569 | | |
570 | 0 | OGRLineString *poLine = new OGRLineString; |
571 | 0 | double dfXLast = 0.0; |
572 | 0 | double dfYLast = 0.0; |
573 | 0 | int nOutCount = 0; |
574 | |
|
575 | 0 | poGeometry = poLine; |
576 | 0 | poLine->setNumPoints(nNumCoord); |
577 | 0 | for (int iCoord = 0; iCoord < nNumCoord; iCoord++) |
578 | 0 | { |
579 | 0 | const int iStart = 14 + iCoord * (GetXYLen() * 2 + 1); |
580 | |
|
581 | 0 | const double dfX = |
582 | 0 | atoi(poRecord->GetField(iStart + 0, iStart + GetXYLen() - 1)) * |
583 | 0 | GetXYMult() + |
584 | 0 | GetXOrigin(); |
585 | 0 | const double dfY = |
586 | 0 | atoi(poRecord->GetField(iStart + GetXYLen(), |
587 | 0 | iStart + GetXYLen() * 2 - 1)) * |
588 | 0 | GetXYMult() + |
589 | 0 | GetYOrigin(); |
590 | |
|
591 | 0 | if (iCoord == 0) |
592 | 0 | { |
593 | 0 | dfXLast = dfX; |
594 | 0 | dfYLast = dfY; |
595 | 0 | poLine->setPoint(nOutCount++, dfX, dfY); |
596 | 0 | } |
597 | 0 | else if (dfXLast != dfX || dfYLast != dfY) |
598 | 0 | { |
599 | 0 | dfXLast = dfX; |
600 | 0 | dfYLast = dfY; |
601 | 0 | poLine->setPoint(nOutCount++, dfX, dfY); |
602 | 0 | } |
603 | 0 | } |
604 | 0 | poLine->setNumPoints(nOutCount); |
605 | |
|
606 | 0 | CacheAddByGeomId(atoi(poRecord->GetField(3, 8)), poLine); |
607 | 0 | } |
608 | | |
609 | | /* -------------------------------------------------------------------- */ |
610 | | /* Arc defined by three points on the arc. */ |
611 | | /* -------------------------------------------------------------------- */ |
612 | 0 | else if (nGType == 5 && nNumCoord == 3) |
613 | 0 | { |
614 | 0 | double adfX[3] = {0.0, 0.0, 0.0}; |
615 | 0 | double adfY[3] = {0.0, 0.0, 0.0}; |
616 | |
|
617 | 0 | for (int iCoord = 0; iCoord < nNumCoord; iCoord++) |
618 | 0 | { |
619 | 0 | const int iStart = 14 + iCoord * (GetXYLen() * 2 + 1); |
620 | |
|
621 | 0 | adfX[iCoord] = |
622 | 0 | atoi(poRecord->GetField(iStart + 0, iStart + GetXYLen() - 1)) * |
623 | 0 | GetXYMult() + |
624 | 0 | GetXOrigin(); |
625 | 0 | adfY[iCoord] = |
626 | 0 | atoi(poRecord->GetField(iStart + GetXYLen(), |
627 | 0 | iStart + GetXYLen() * 2 - 1)) * |
628 | 0 | GetXYMult() + |
629 | 0 | GetYOrigin(); |
630 | 0 | } |
631 | |
|
632 | 0 | poGeometry = NTFStrokeArcToOGRGeometry_Points( |
633 | 0 | adfX[0], adfY[0], adfX[1], adfY[1], adfX[2], adfY[2], 72); |
634 | 0 | } |
635 | | |
636 | | /* -------------------------------------------------------------------- */ |
637 | | /* Circle */ |
638 | | /* -------------------------------------------------------------------- */ |
639 | 0 | else if (nGType == 7) |
640 | 0 | { |
641 | 0 | const int iCenterStart = 14; |
642 | 0 | const int iArcStart = 14 + 2 * GetXYLen() + 1; |
643 | |
|
644 | 0 | const double dfCenterX = |
645 | 0 | atoi(poRecord->GetField(iCenterStart, |
646 | 0 | iCenterStart + GetXYLen() - 1)) * |
647 | 0 | GetXYMult() + |
648 | 0 | GetXOrigin(); |
649 | 0 | const double dfCenterY = |
650 | 0 | atoi(poRecord->GetField(iCenterStart + GetXYLen(), |
651 | 0 | iCenterStart + GetXYLen() * 2 - 1)) * |
652 | 0 | GetXYMult() + |
653 | 0 | GetYOrigin(); |
654 | |
|
655 | 0 | const double dfArcX = |
656 | 0 | atoi(poRecord->GetField(iArcStart, iArcStart + GetXYLen() - 1)) * |
657 | 0 | GetXYMult() + |
658 | 0 | GetXOrigin(); |
659 | 0 | const double dfArcY = |
660 | 0 | atoi(poRecord->GetField(iArcStart + GetXYLen(), |
661 | 0 | iArcStart + GetXYLen() * 2 - 1)) * |
662 | 0 | GetXYMult() + |
663 | 0 | GetYOrigin(); |
664 | |
|
665 | 0 | const double dfRadius = |
666 | 0 | sqrt((dfCenterX - dfArcX) * (dfCenterX - dfArcX) + |
667 | 0 | (dfCenterY - dfArcY) * (dfCenterY - dfArcY)); |
668 | |
|
669 | 0 | poGeometry = NTFStrokeArcToOGRGeometry_Angles(dfCenterX, dfCenterY, |
670 | 0 | dfRadius, 0.0, 360.0, 72); |
671 | 0 | } |
672 | | |
673 | 0 | else |
674 | 0 | { |
675 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Unhandled GType = %d", nGType); |
676 | 0 | } |
677 | | |
678 | 0 | if (poGeometry != nullptr) |
679 | 0 | poGeometry->assignSpatialReference(poDS->DSGetSpatialRef()); |
680 | |
|
681 | 0 | return poGeometry; |
682 | 0 | } |
683 | | |
684 | | /************************************************************************/ |
685 | | /* ProcessGeometry3D() */ |
686 | | /************************************************************************/ |
687 | | |
688 | | OGRGeometry *NTFFileReader::ProcessGeometry3D(NTFRecord *poRecord, |
689 | | int *pnGeomId) |
690 | | |
691 | 0 | { |
692 | 0 | OGRGeometry *poGeometry = nullptr; |
693 | |
|
694 | 0 | if (poRecord->GetType() != NRT_GEOMETRY3D) |
695 | 0 | return nullptr; |
696 | | |
697 | 0 | const int nGType = atoi(poRecord->GetField(9, 9)); // GTYPE |
698 | 0 | const int nNumCoord = atoi(poRecord->GetField(10, 13)); // NUM_COORD |
699 | 0 | if (pnGeomId != nullptr) |
700 | 0 | *pnGeomId = atoi(poRecord->GetField(3, 8)); // GEOM_ID |
701 | |
|
702 | 0 | if (nGType == 1) |
703 | 0 | { |
704 | 0 | if (14 + 1 + 2 * static_cast<GIntBig>(GetXYLen()) + nZWidth - 1 > |
705 | 0 | INT_MAX) |
706 | 0 | { |
707 | 0 | return nullptr; |
708 | 0 | } |
709 | 0 | const double dfX = |
710 | 0 | atoi(poRecord->GetField(14, 14 + GetXYLen() - 1)) * GetXYMult() + |
711 | 0 | GetXOrigin(); |
712 | 0 | const double dfY = |
713 | 0 | atoi(poRecord->GetField(14 + GetXYLen(), 14 + GetXYLen() * 2 - 1)) * |
714 | 0 | GetXYMult() + |
715 | 0 | GetYOrigin(); |
716 | 0 | const double dfZ = |
717 | 0 | atoi(poRecord->GetField(14 + 1 + 2 * GetXYLen(), |
718 | 0 | 14 + 1 + 2 * GetXYLen() + nZWidth - 1)) * |
719 | 0 | dfZMult; |
720 | |
|
721 | 0 | poGeometry = new OGRPoint(dfX, dfY, dfZ); |
722 | 0 | } |
723 | | |
724 | 0 | else if (nGType == 2) |
725 | 0 | { |
726 | 0 | if (nNumCoord < 0 || 14 + |
727 | 0 | static_cast<GIntBig>(nNumCoord - 1) * |
728 | 0 | (GetXYLen() * 2 + nZWidth + 2) + |
729 | 0 | 1 + 2 * GetXYLen() + nZWidth - 1 > |
730 | 0 | INT_MAX) |
731 | 0 | { |
732 | 0 | return nullptr; |
733 | 0 | } |
734 | | |
735 | 0 | OGRLineString *poLine = new OGRLineString; |
736 | 0 | double dfXLast = 0.0; |
737 | 0 | double dfYLast = 0.0; |
738 | 0 | int nOutCount = 0; |
739 | |
|
740 | 0 | poGeometry = poLine; |
741 | 0 | poLine->setNumPoints(nNumCoord); |
742 | 0 | const GUInt32 nErrorsBefore = CPLGetErrorCounter(); |
743 | 0 | for (int iCoord = 0; iCoord < nNumCoord; iCoord++) |
744 | 0 | { |
745 | 0 | const int iStart = 14 + iCoord * (GetXYLen() * 2 + nZWidth + 2); |
746 | |
|
747 | 0 | const char *pszX = |
748 | 0 | poRecord->GetField(iStart + 0, iStart + GetXYLen() - 1); |
749 | 0 | bool bSpace = pszX[0] == ' '; |
750 | 0 | const double dfX = atoi(pszX) * GetXYMult() + GetXOrigin(); |
751 | 0 | const char *pszY = poRecord->GetField(iStart + GetXYLen(), |
752 | 0 | iStart + GetXYLen() * 2 - 1); |
753 | 0 | bSpace |= pszY[0] == ' '; |
754 | 0 | const double dfY = atoi(pszY) * GetXYMult() + GetYOrigin(); |
755 | |
|
756 | 0 | const char *pszZ = |
757 | 0 | poRecord->GetField(iStart + 1 + 2 * GetXYLen(), |
758 | 0 | iStart + 1 + 2 * GetXYLen() + nZWidth - 1); |
759 | 0 | bSpace |= pszZ[0] == ' '; |
760 | 0 | const double dfZ = atoi(pszZ) * dfZMult; |
761 | 0 | if (bSpace && CPLGetErrorCounter() != nErrorsBefore) |
762 | 0 | { |
763 | 0 | delete poGeometry; |
764 | 0 | return nullptr; |
765 | 0 | } |
766 | | |
767 | 0 | if (iCoord == 0) |
768 | 0 | { |
769 | 0 | dfXLast = dfX; |
770 | 0 | dfYLast = dfY; |
771 | 0 | poLine->setPoint(nOutCount++, dfX, dfY, dfZ); |
772 | 0 | } |
773 | 0 | else if (dfXLast != dfX || dfYLast != dfY) |
774 | 0 | { |
775 | 0 | dfXLast = dfX; |
776 | 0 | dfYLast = dfY; |
777 | 0 | poLine->setPoint(nOutCount++, dfX, dfY, dfZ); |
778 | 0 | } |
779 | 0 | } |
780 | 0 | poLine->setNumPoints(nOutCount); |
781 | |
|
782 | 0 | CacheAddByGeomId(atoi(poRecord->GetField(3, 8)), poLine); |
783 | 0 | } |
784 | | |
785 | 0 | if (poGeometry != nullptr) |
786 | 0 | poGeometry->assignSpatialReference(poDS->DSGetSpatialRef()); |
787 | |
|
788 | 0 | return poGeometry; |
789 | 0 | } |
790 | | |
791 | | /************************************************************************/ |
792 | | /* ProcessAttDesc() */ |
793 | | /************************************************************************/ |
794 | | |
795 | | int NTFFileReader::ProcessAttDesc(NTFRecord *poRecord, NTFAttDesc *psAD) |
796 | | |
797 | 0 | { |
798 | 0 | psAD->poCodeList = nullptr; |
799 | 0 | if (poRecord->GetType() != NRT_ADR || poRecord->GetLength() < 13) |
800 | 0 | return FALSE; |
801 | | |
802 | 0 | snprintf(psAD->val_type, sizeof(psAD->val_type), "%s", |
803 | 0 | poRecord->GetField(3, 4)); |
804 | 0 | snprintf(psAD->fwidth, sizeof(psAD->fwidth), "%s", |
805 | 0 | poRecord->GetField(5, 7)); |
806 | 0 | snprintf(psAD->finter, sizeof(psAD->finter), "%s", |
807 | 0 | poRecord->GetField(8, 12)); |
808 | |
|
809 | 0 | const char *pszData = poRecord->GetData(); |
810 | 0 | int iChar = 12; // Used after for. |
811 | 0 | for (; pszData[iChar] != '\0' && pszData[iChar] != '\\'; iChar++) |
812 | 0 | { |
813 | 0 | } |
814 | |
|
815 | 0 | snprintf(psAD->att_name, sizeof(psAD->att_name), "%s", |
816 | 0 | poRecord->GetField(13, iChar)); |
817 | |
|
818 | 0 | return TRUE; |
819 | 0 | } |
820 | | |
821 | | /************************************************************************/ |
822 | | /* ProcessAttRecGroup() */ |
823 | | /* */ |
824 | | /* Extract attribute values from all attribute records in a */ |
825 | | /* record set. */ |
826 | | /************************************************************************/ |
827 | | |
828 | | int NTFFileReader::ProcessAttRecGroup(NTFRecord **papoRecords, |
829 | | char ***ppapszTypes, char ***ppapszValues) |
830 | | |
831 | 0 | { |
832 | 0 | *ppapszTypes = nullptr; |
833 | 0 | *ppapszValues = nullptr; |
834 | |
|
835 | 0 | for (int iRec = 0; papoRecords[iRec] != nullptr; iRec++) |
836 | 0 | { |
837 | 0 | if (papoRecords[iRec]->GetType() != NRT_ATTREC) |
838 | 0 | continue; |
839 | | |
840 | 0 | char **papszTypes1 = nullptr; |
841 | 0 | char **papszValues1 = nullptr; |
842 | 0 | if (!ProcessAttRec(papoRecords[iRec], nullptr, &papszTypes1, |
843 | 0 | &papszValues1)) |
844 | 0 | { |
845 | 0 | CSLDestroy(*ppapszTypes); |
846 | 0 | CSLDestroy(*ppapszValues); |
847 | 0 | *ppapszTypes = nullptr; |
848 | 0 | *ppapszValues = nullptr; |
849 | 0 | return FALSE; |
850 | 0 | } |
851 | | |
852 | 0 | if (*ppapszTypes == nullptr) |
853 | 0 | { |
854 | 0 | *ppapszTypes = papszTypes1; |
855 | 0 | *ppapszValues = papszValues1; |
856 | 0 | } |
857 | 0 | else |
858 | 0 | { |
859 | 0 | for (int i = 0; papszTypes1[i] != nullptr; i++) |
860 | 0 | { |
861 | 0 | *ppapszTypes = CSLAddString(*ppapszTypes, papszTypes1[i]); |
862 | 0 | *ppapszValues = CSLAddString(*ppapszValues, papszValues1[i]); |
863 | 0 | } |
864 | 0 | CSLDestroy(papszTypes1); |
865 | 0 | CSLDestroy(papszValues1); |
866 | 0 | } |
867 | 0 | } |
868 | | |
869 | 0 | return TRUE; |
870 | 0 | } |
871 | | |
872 | | /************************************************************************/ |
873 | | /* ProcessAttRec() */ |
874 | | /************************************************************************/ |
875 | | |
876 | | int NTFFileReader::ProcessAttRec(NTFRecord *poRecord, int *pnAttId, |
877 | | char ***ppapszTypes, char ***ppapszValues) |
878 | | |
879 | 0 | { |
880 | 0 | if (pnAttId != nullptr) |
881 | 0 | *pnAttId = 0; |
882 | 0 | *ppapszTypes = nullptr; |
883 | 0 | *ppapszValues = nullptr; |
884 | |
|
885 | 0 | if (poRecord->GetType() != NRT_ATTREC || poRecord->GetLength() < 8) |
886 | 0 | return FALSE; |
887 | | |
888 | | /* -------------------------------------------------------------------- */ |
889 | | /* Extract the attribute id. */ |
890 | | /* -------------------------------------------------------------------- */ |
891 | 0 | if (pnAttId != nullptr) |
892 | 0 | *pnAttId = atoi(poRecord->GetField(3, 8)); |
893 | | |
894 | | /* ==================================================================== */ |
895 | | /* Loop handling attribute till we get a '0' indicating the end */ |
896 | | /* of the record. */ |
897 | | /* ==================================================================== */ |
898 | |
|
899 | 0 | int iOffset = 8; |
900 | 0 | const char *pszData = poRecord->GetData(); |
901 | 0 | bool bError = false; |
902 | |
|
903 | 0 | while (iOffset < poRecord->GetLength() && pszData[iOffset] != DIGIT_ZERO) |
904 | 0 | { |
905 | | /* -------------------------------------------------------------------- |
906 | | */ |
907 | | /* Extract the two letter code name for the attribute, and use */ |
908 | | /* it to find the correct ATTDESC info. */ |
909 | | /* -------------------------------------------------------------------- |
910 | | */ |
911 | 0 | NTFAttDesc *psAttDesc = GetAttDesc(pszData + iOffset); |
912 | 0 | if (psAttDesc == nullptr) |
913 | 0 | { |
914 | 0 | CPLDebug("NTF", "Couldn't translate attrec type `%2.2s'.", |
915 | 0 | pszData + iOffset); |
916 | 0 | bError = true; |
917 | 0 | break; |
918 | 0 | } |
919 | | |
920 | 0 | *ppapszTypes = CSLAddString( |
921 | 0 | *ppapszTypes, poRecord->GetField(iOffset + 1, iOffset + 2)); |
922 | | |
923 | | /* -------------------------------------------------------------------- |
924 | | */ |
925 | | /* Establish the width of the value. Zero width fields are */ |
926 | | /* terminated by a backslash. */ |
927 | | /* -------------------------------------------------------------------- |
928 | | */ |
929 | 0 | const int nFWidth = atoi(psAttDesc->fwidth); |
930 | 0 | if (nFWidth < 0) |
931 | 0 | { |
932 | 0 | bError = true; |
933 | 0 | break; |
934 | 0 | } |
935 | 0 | int nEnd = 0; |
936 | 0 | if (nFWidth == 0) |
937 | 0 | { |
938 | 0 | const char *pszData2 = poRecord->GetData(); |
939 | 0 | if (iOffset + 2 >= poRecord->GetLength()) |
940 | 0 | { |
941 | 0 | bError = true; |
942 | 0 | break; |
943 | 0 | } |
944 | 0 | for (nEnd = iOffset + 2; |
945 | 0 | pszData2[nEnd] != '\\' && pszData2[nEnd] != '\0'; nEnd++) |
946 | 0 | { |
947 | 0 | } |
948 | 0 | } |
949 | 0 | else |
950 | 0 | { |
951 | 0 | nEnd = iOffset + 3 + nFWidth - 1; |
952 | 0 | } |
953 | | |
954 | | /* -------------------------------------------------------------------- |
955 | | */ |
956 | | /* Extract the value. If it is formatted as fixed point real */ |
957 | | /* we reprocess it to insert the decimal point. */ |
958 | | /* -------------------------------------------------------------------- |
959 | | */ |
960 | 0 | const char *pszRawValue = poRecord->GetField(iOffset + 3, nEnd); |
961 | 0 | *ppapszValues = CSLAddString(*ppapszValues, pszRawValue); |
962 | | |
963 | | /* -------------------------------------------------------------------- |
964 | | */ |
965 | | /* Establish new offset position. */ |
966 | | /* -------------------------------------------------------------------- |
967 | | */ |
968 | 0 | if (nFWidth == 0) |
969 | 0 | { |
970 | 0 | iOffset = nEnd; |
971 | 0 | if (iOffset >= poRecord->GetLength()) |
972 | 0 | { |
973 | 0 | bError = (iOffset > poRecord->GetLength()); |
974 | 0 | break; |
975 | 0 | } |
976 | 0 | if (pszData[iOffset] == '\\') |
977 | 0 | iOffset++; |
978 | 0 | } |
979 | 0 | else |
980 | 0 | iOffset += 2 + nFWidth; |
981 | 0 | } |
982 | |
|
983 | 0 | if (bError) |
984 | 0 | { |
985 | 0 | CSLDestroy(*ppapszTypes); |
986 | 0 | CSLDestroy(*ppapszValues); |
987 | 0 | *ppapszTypes = nullptr; |
988 | 0 | *ppapszValues = nullptr; |
989 | 0 | } |
990 | |
|
991 | 0 | return (*ppapszTypes != nullptr); |
992 | 0 | } |
993 | | |
994 | | /************************************************************************/ |
995 | | /* GetAttDesc() */ |
996 | | /************************************************************************/ |
997 | | |
998 | | NTFAttDesc *NTFFileReader::GetAttDesc(const char *pszType) |
999 | | |
1000 | 0 | { |
1001 | 0 | for (int i = 0; i < nAttCount; i++) |
1002 | 0 | { |
1003 | 0 | if (EQUALN(pszType, pasAttDesc[i].val_type, 2)) |
1004 | 0 | return pasAttDesc + i; |
1005 | 0 | } |
1006 | | |
1007 | 0 | return nullptr; |
1008 | 0 | } |
1009 | | |
1010 | | /************************************************************************/ |
1011 | | /* ProcessAttValue() */ |
1012 | | /* */ |
1013 | | /* Take an attribute type/value pair and transform into a */ |
1014 | | /* meaningful attribute name, and value. The source can be an */ |
1015 | | /* ATTREC or the VAL_TYPE/VALUE pair of a POINTREC or LINEREC. */ |
1016 | | /* The name is transformed from the two character short form to */ |
1017 | | /* the long user name. The value will be transformed from */ |
1018 | | /* fixed point (with the decimal implicit) to fixed point with */ |
1019 | | /* an explicit decimal point if it has a "R" format. */ |
1020 | | /* Note: the returned *ppszAttValue has a very short lifetime */ |
1021 | | /* and should immediately be used. Further calls to */ |
1022 | | /* ProcessAttValue or CPLSPrintf() will invalidate it. */ |
1023 | | /************************************************************************/ |
1024 | | |
1025 | | int NTFFileReader::ProcessAttValue(const char *pszValType, |
1026 | | const char *pszRawValue, |
1027 | | const char **ppszAttName, |
1028 | | const char **ppszAttValue, |
1029 | | const char **ppszCodeDesc) |
1030 | | |
1031 | 0 | { |
1032 | | /* -------------------------------------------------------------------- */ |
1033 | | /* Find the ATTDESC for this attribute, and assign return name value.*/ |
1034 | | /* -------------------------------------------------------------------- */ |
1035 | 0 | NTFAttDesc *psAttDesc = GetAttDesc(pszValType); |
1036 | |
|
1037 | 0 | if (psAttDesc == nullptr) |
1038 | 0 | return FALSE; |
1039 | | |
1040 | 0 | if (ppszAttName != nullptr) |
1041 | 0 | *ppszAttName = psAttDesc->att_name; |
1042 | | |
1043 | | /* -------------------------------------------------------------------- */ |
1044 | | /* Extract the value. If it is formatted as fixed point real */ |
1045 | | /* we reprocess it to insert the decimal point. */ |
1046 | | /* -------------------------------------------------------------------- */ |
1047 | 0 | if (psAttDesc->finter[0] == 'R') |
1048 | 0 | { |
1049 | 0 | const char *pszDecimalPortion = nullptr; // Used after for. |
1050 | |
|
1051 | 0 | for (pszDecimalPortion = psAttDesc->finter; |
1052 | 0 | *pszDecimalPortion != ',' && *pszDecimalPortion != '\0'; |
1053 | 0 | pszDecimalPortion++) |
1054 | 0 | { |
1055 | 0 | } |
1056 | 0 | if (*pszDecimalPortion == '\0') |
1057 | 0 | { |
1058 | 0 | *ppszAttValue = ""; |
1059 | 0 | } |
1060 | 0 | else |
1061 | 0 | { |
1062 | 0 | const int nWidth = static_cast<int>(strlen(pszRawValue)); |
1063 | 0 | const int nPrecision = atoi(pszDecimalPortion + 1); |
1064 | 0 | if (nPrecision < 0 || nPrecision >= nWidth) |
1065 | 0 | { |
1066 | 0 | *ppszAttValue = ""; |
1067 | 0 | } |
1068 | 0 | else |
1069 | 0 | { |
1070 | 0 | CPLString osResult(pszRawValue); |
1071 | 0 | osResult.resize(nWidth - nPrecision); |
1072 | 0 | osResult += "."; |
1073 | 0 | osResult += pszRawValue + nWidth - nPrecision; |
1074 | |
|
1075 | 0 | *ppszAttValue = CPLSPrintf("%s", osResult.c_str()); |
1076 | 0 | } |
1077 | 0 | } |
1078 | 0 | } |
1079 | | |
1080 | | /* -------------------------------------------------------------------- */ |
1081 | | /* If it is an integer, we just reformat to get rid of leading */ |
1082 | | /* zeros. */ |
1083 | | /* -------------------------------------------------------------------- */ |
1084 | 0 | else if (psAttDesc->finter[0] == 'I') |
1085 | 0 | { |
1086 | 0 | *ppszAttValue = CPLSPrintf("%d", atoi(pszRawValue)); |
1087 | 0 | } |
1088 | | |
1089 | | /* -------------------------------------------------------------------- */ |
1090 | | /* Otherwise we take the value directly. */ |
1091 | | /* -------------------------------------------------------------------- */ |
1092 | 0 | else |
1093 | 0 | { |
1094 | 0 | *ppszAttValue = pszRawValue; |
1095 | 0 | } |
1096 | | |
1097 | | /* -------------------------------------------------------------------- */ |
1098 | | /* Handle processing code values into code descriptions, if */ |
1099 | | /* applicable. */ |
1100 | | /* -------------------------------------------------------------------- */ |
1101 | 0 | if (ppszCodeDesc == nullptr) |
1102 | 0 | { |
1103 | 0 | } |
1104 | 0 | else if (psAttDesc->poCodeList != nullptr) |
1105 | 0 | { |
1106 | 0 | *ppszCodeDesc = psAttDesc->poCodeList->Lookup(*ppszAttValue); |
1107 | 0 | } |
1108 | 0 | else |
1109 | 0 | { |
1110 | 0 | *ppszCodeDesc = nullptr; |
1111 | 0 | } |
1112 | |
|
1113 | 0 | return TRUE; |
1114 | 0 | } |
1115 | | |
1116 | | /************************************************************************/ |
1117 | | /* ApplyAttributeValues() */ |
1118 | | /* */ |
1119 | | /* Apply a series of attribute values to a feature from generic */ |
1120 | | /* attribute records. */ |
1121 | | /************************************************************************/ |
1122 | | |
1123 | | void NTFFileReader::ApplyAttributeValues(OGRFeature *poFeature, |
1124 | | NTFRecord **papoGroup, ...) |
1125 | | |
1126 | 0 | { |
1127 | | /* -------------------------------------------------------------------- */ |
1128 | | /* Extract attribute values from record group. */ |
1129 | | /* -------------------------------------------------------------------- */ |
1130 | 0 | char **papszTypes = nullptr; |
1131 | 0 | char **papszValues = nullptr; |
1132 | |
|
1133 | 0 | if (!ProcessAttRecGroup(papoGroup, &papszTypes, &papszValues)) |
1134 | 0 | return; |
1135 | | |
1136 | | /* -------------------------------------------------------------------- */ |
1137 | | /* Handle attribute pairs */ |
1138 | | /* -------------------------------------------------------------------- */ |
1139 | 0 | va_list hVaArgs; |
1140 | |
|
1141 | 0 | va_start(hVaArgs, papoGroup); |
1142 | |
|
1143 | 0 | const char *pszAttName = nullptr; |
1144 | 0 | while ((pszAttName = va_arg(hVaArgs, const char *)) != nullptr) |
1145 | 0 | { |
1146 | 0 | const int iField = va_arg(hVaArgs, int); |
1147 | |
|
1148 | 0 | ApplyAttributeValue(poFeature, iField, pszAttName, papszTypes, |
1149 | 0 | papszValues); |
1150 | 0 | } |
1151 | |
|
1152 | 0 | va_end(hVaArgs); |
1153 | | |
1154 | | /* -------------------------------------------------------------------- */ |
1155 | | /* Cleanup. */ |
1156 | | /* -------------------------------------------------------------------- */ |
1157 | 0 | CSLDestroy(papszTypes); |
1158 | 0 | CSLDestroy(papszValues); |
1159 | 0 | } |
1160 | | |
1161 | | /************************************************************************/ |
1162 | | /* ApplyAttributeValue() */ |
1163 | | /* */ |
1164 | | /* Apply the indicated attribute value to an OGRFeature field */ |
1165 | | /* if it exists in the attribute value list given. */ |
1166 | | /************************************************************************/ |
1167 | | |
1168 | | int NTFFileReader::ApplyAttributeValue(OGRFeature *poFeature, int iField, |
1169 | | const char *pszAttName, |
1170 | | char **papszTypes, char **papszValues) |
1171 | | |
1172 | 0 | { |
1173 | | /* -------------------------------------------------------------------- */ |
1174 | | /* Find the requested attribute in the name/value pair */ |
1175 | | /* provided. If not found that's fine, just return with */ |
1176 | | /* notification. */ |
1177 | | /* -------------------------------------------------------------------- */ |
1178 | 0 | const int iValue = CSLFindString(papszTypes, pszAttName); |
1179 | 0 | if (iValue < 0) |
1180 | 0 | return FALSE; |
1181 | | |
1182 | 0 | CPLAssert(papszValues != nullptr); |
1183 | | /* -------------------------------------------------------------------- */ |
1184 | | /* Process the attribute value ... this really only has a */ |
1185 | | /* useful effect for real numbers. */ |
1186 | | /* -------------------------------------------------------------------- */ |
1187 | 0 | const char *pszAttLongName = nullptr; |
1188 | 0 | const char *pszAttValue = nullptr; |
1189 | 0 | const char *pszCodeDesc = nullptr; |
1190 | |
|
1191 | 0 | if (!ProcessAttValue(pszAttName, papszValues[iValue], &pszAttLongName, |
1192 | 0 | &pszAttValue, &pszCodeDesc)) |
1193 | 0 | return FALSE; |
1194 | | |
1195 | | /* -------------------------------------------------------------------- */ |
1196 | | /* Apply the value to the field using the simple set string */ |
1197 | | /* method. Leave it to the OGRFeature::SetField() method to */ |
1198 | | /* take care of translation to other types. */ |
1199 | | /* -------------------------------------------------------------------- */ |
1200 | 0 | poFeature->SetField(iField, pszAttValue); |
1201 | | |
1202 | | /* -------------------------------------------------------------------- */ |
1203 | | /* Apply the code description if we found one. */ |
1204 | | /* -------------------------------------------------------------------- */ |
1205 | 0 | if (pszCodeDesc != nullptr) |
1206 | 0 | { |
1207 | 0 | char szDescFieldName[256]; |
1208 | |
|
1209 | 0 | snprintf(szDescFieldName, sizeof(szDescFieldName), "%s_DESC", |
1210 | 0 | poFeature->GetDefnRef()->GetFieldDefn(iField)->GetNameRef()); |
1211 | 0 | poFeature->SetField(szDescFieldName, pszCodeDesc); |
1212 | 0 | } |
1213 | |
|
1214 | 0 | return TRUE; |
1215 | 0 | } |
1216 | | |
1217 | | /************************************************************************/ |
1218 | | /* SaveRecord() */ |
1219 | | /************************************************************************/ |
1220 | | |
1221 | | void NTFFileReader::SaveRecord(NTFRecord *poRecord) |
1222 | | |
1223 | 0 | { |
1224 | 0 | CPLAssert(poSavedRecord == nullptr); |
1225 | 0 | poSavedRecord = poRecord; |
1226 | 0 | } |
1227 | | |
1228 | | /************************************************************************/ |
1229 | | /* ReadRecord() */ |
1230 | | /************************************************************************/ |
1231 | | |
1232 | | NTFRecord *NTFFileReader::ReadRecord() |
1233 | | |
1234 | 0 | { |
1235 | 0 | if (poSavedRecord != nullptr) |
1236 | 0 | { |
1237 | 0 | NTFRecord *poReturn = poSavedRecord; |
1238 | |
|
1239 | 0 | poSavedRecord = nullptr; |
1240 | |
|
1241 | 0 | return poReturn; |
1242 | 0 | } |
1243 | 0 | else |
1244 | 0 | { |
1245 | 0 | CPLErrorReset(); |
1246 | 0 | if (fp != nullptr) |
1247 | 0 | nPreSavedPos = VSIFTellL(fp); |
1248 | 0 | NTFRecord *poRecord = new NTFRecord(fp); |
1249 | 0 | if (fp != nullptr) |
1250 | 0 | nPostSavedPos = VSIFTellL(fp); |
1251 | | |
1252 | | /* ensure termination if we fail to read a record */ |
1253 | 0 | if (CPLGetLastErrorType() == CE_Failure) |
1254 | 0 | { |
1255 | 0 | delete poRecord; |
1256 | 0 | poRecord = nullptr; |
1257 | 0 | } |
1258 | |
|
1259 | 0 | return poRecord; |
1260 | 0 | } |
1261 | 0 | } |
1262 | | |
1263 | | /************************************************************************/ |
1264 | | /* GetFPPos() */ |
1265 | | /* */ |
1266 | | /* Return the current file pointer position. */ |
1267 | | /************************************************************************/ |
1268 | | |
1269 | | void NTFFileReader::GetFPPos(vsi_l_offset *pnPos, long *pnFID) |
1270 | | |
1271 | 0 | { |
1272 | 0 | if (poSavedRecord != nullptr) |
1273 | 0 | *pnPos = nPreSavedPos; |
1274 | 0 | else |
1275 | 0 | *pnPos = nPostSavedPos; |
1276 | |
|
1277 | 0 | if (pnFID != nullptr) |
1278 | 0 | *pnFID = nSavedFeatureId; |
1279 | 0 | } |
1280 | | |
1281 | | /************************************************************************/ |
1282 | | /* SetFPPos() */ |
1283 | | /************************************************************************/ |
1284 | | |
1285 | | int NTFFileReader::SetFPPos(vsi_l_offset nNewPos, long nNewFID) |
1286 | | |
1287 | 0 | { |
1288 | 0 | if (nNewFID == nSavedFeatureId) |
1289 | 0 | return TRUE; |
1290 | | |
1291 | 0 | if (poSavedRecord != nullptr) |
1292 | 0 | { |
1293 | 0 | delete poSavedRecord; |
1294 | 0 | poSavedRecord = nullptr; |
1295 | 0 | } |
1296 | |
|
1297 | 0 | if (fp != nullptr && VSIFSeekL(fp, nNewPos, SEEK_SET) == 0) |
1298 | 0 | { |
1299 | 0 | nPreSavedPos = nPostSavedPos = nNewPos; |
1300 | 0 | nSavedFeatureId = nNewFID; |
1301 | 0 | return TRUE; |
1302 | 0 | } |
1303 | 0 | else |
1304 | 0 | return FALSE; |
1305 | 0 | } |
1306 | | |
1307 | | /************************************************************************/ |
1308 | | /* Reset() */ |
1309 | | /* */ |
1310 | | /* Reset reading to the first feature record. */ |
1311 | | /************************************************************************/ |
1312 | | |
1313 | | void NTFFileReader::Reset() |
1314 | | |
1315 | 0 | { |
1316 | 0 | SetFPPos(nStartPos, nBaseFeatureId); |
1317 | 0 | ClearCGroup(); |
1318 | 0 | } |
1319 | | |
1320 | | /************************************************************************/ |
1321 | | /* ClearCGroup() */ |
1322 | | /* */ |
1323 | | /* Clear the currently loaded record group. */ |
1324 | | /************************************************************************/ |
1325 | | |
1326 | | void NTFFileReader::ClearCGroup() |
1327 | | |
1328 | 4 | { |
1329 | 4 | for (int i = 0; apoCGroup[i] != nullptr; i++) |
1330 | 0 | delete apoCGroup[i]; |
1331 | | |
1332 | 4 | apoCGroup[0] = nullptr; |
1333 | 4 | apoCGroup[1] = nullptr; |
1334 | 4 | } |
1335 | | |
1336 | | /************************************************************************/ |
1337 | | /* DefaultNTFRecordGrouper() */ |
1338 | | /* */ |
1339 | | /* Default rules for figuring out if a new candidate record */ |
1340 | | /* belongs to a group of records that together form a feature */ |
1341 | | /* (a record group). */ |
1342 | | /************************************************************************/ |
1343 | | |
1344 | | int DefaultNTFRecordGrouper(NTFFileReader *, NTFRecord **papoGroup, |
1345 | | NTFRecord *poCandidate) |
1346 | | |
1347 | 0 | { |
1348 | | /* -------------------------------------------------------------------- */ |
1349 | | /* Is this group going to be a CPOLY set? We can recognise */ |
1350 | | /* this because we get repeating POLY/CHAIN sets without an */ |
1351 | | /* intermediate attribute record. This is a rather special case! */ |
1352 | | /* -------------------------------------------------------------------- */ |
1353 | 0 | if (papoGroup[0] != nullptr && papoGroup[1] != nullptr && |
1354 | 0 | papoGroup[0]->GetType() == NRT_POLYGON && |
1355 | 0 | papoGroup[1]->GetType() == NRT_CHAIN) |
1356 | 0 | { |
1357 | | // We keep going till we get the seed geometry. |
1358 | 0 | int iRec, bGotCPOLY = FALSE; |
1359 | |
|
1360 | 0 | for (iRec = 0; papoGroup[iRec] != nullptr; iRec++) |
1361 | 0 | { |
1362 | 0 | if (papoGroup[iRec]->GetType() == NRT_CPOLY) |
1363 | 0 | bGotCPOLY = TRUE; |
1364 | 0 | } |
1365 | |
|
1366 | 0 | if (bGotCPOLY && poCandidate->GetType() != NRT_GEOMETRY && |
1367 | 0 | poCandidate->GetType() != NRT_ATTREC) |
1368 | 0 | return FALSE; |
1369 | | |
1370 | | /* |
1371 | | * this logic assumes we always get a point geometry with a CPOLY |
1372 | | * but that isn't always true, for instance with BL2000 data. The |
1373 | | * preceding check will handle this case. |
1374 | | */ |
1375 | 0 | if (papoGroup[iRec - 1]->GetType() != NRT_GEOMETRY) |
1376 | 0 | return TRUE; |
1377 | 0 | else |
1378 | 0 | return FALSE; |
1379 | 0 | } |
1380 | | |
1381 | | /* -------------------------------------------------------------------- */ |
1382 | | /* Is this a "feature" defining record? If so break out if it */ |
1383 | | /* isn't the first record in the group. */ |
1384 | | /* -------------------------------------------------------------------- */ |
1385 | 0 | if (papoGroup[0] != nullptr && (poCandidate->GetType() == NRT_NAMEREC || |
1386 | 0 | poCandidate->GetType() == NRT_NODEREC || |
1387 | 0 | poCandidate->GetType() == NRT_LINEREC || |
1388 | 0 | poCandidate->GetType() == NRT_POINTREC || |
1389 | 0 | poCandidate->GetType() == NRT_POLYGON || |
1390 | 0 | poCandidate->GetType() == NRT_CPOLY || |
1391 | 0 | poCandidate->GetType() == NRT_COLLECT || |
1392 | 0 | poCandidate->GetType() == NRT_TEXTREC || |
1393 | 0 | poCandidate->GetType() == NRT_COMMENT)) |
1394 | 0 | { |
1395 | 0 | return FALSE; |
1396 | 0 | } |
1397 | | |
1398 | | /* -------------------------------------------------------------------- */ |
1399 | | /* Do we already have a record of this type? If so, it likely */ |
1400 | | /* doesn't belong in the group. Attribute records do repeat in */ |
1401 | | /* some products. */ |
1402 | | /* -------------------------------------------------------------------- */ |
1403 | 0 | if (poCandidate->GetType() != NRT_ATTREC) |
1404 | 0 | { |
1405 | 0 | int iRec = 0; // Used after for. |
1406 | 0 | for (; papoGroup[iRec] != nullptr; iRec++) |
1407 | 0 | { |
1408 | 0 | if (poCandidate->GetType() == papoGroup[iRec]->GetType()) |
1409 | 0 | break; |
1410 | 0 | } |
1411 | |
|
1412 | 0 | if (papoGroup[iRec] != nullptr) |
1413 | 0 | return FALSE; |
1414 | 0 | } |
1415 | | |
1416 | 0 | return TRUE; |
1417 | 0 | } |
1418 | | |
1419 | | /************************************************************************/ |
1420 | | /* ReadRecordGroup() */ |
1421 | | /* */ |
1422 | | /* Read a group of records that form a single feature. */ |
1423 | | /************************************************************************/ |
1424 | | |
1425 | | NTFRecord **NTFFileReader::ReadRecordGroup() |
1426 | | |
1427 | 0 | { |
1428 | |
|
1429 | 0 | ClearCGroup(); |
1430 | | |
1431 | | /* -------------------------------------------------------------------- */ |
1432 | | /* Loop, reading records till we think we have a grouping. */ |
1433 | | /* -------------------------------------------------------------------- */ |
1434 | 0 | int nRecordCount = 0; |
1435 | 0 | NTFRecord *poRecord = nullptr; |
1436 | 0 | while ((poRecord = ReadRecord()) != nullptr && |
1437 | 0 | poRecord->GetType() != NRT_VTR) |
1438 | 0 | { |
1439 | 0 | if (nRecordCount >= MAX_REC_GROUP) |
1440 | 0 | { |
1441 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1442 | 0 | "Maximum record group size (%d) exceeded.\n", |
1443 | 0 | MAX_REC_GROUP); |
1444 | 0 | break; |
1445 | 0 | } |
1446 | | |
1447 | 0 | if (!pfnRecordGrouper(this, apoCGroup, poRecord)) |
1448 | 0 | break; |
1449 | | |
1450 | 0 | apoCGroup[nRecordCount++] = poRecord; |
1451 | 0 | apoCGroup[nRecordCount] = nullptr; |
1452 | 0 | } |
1453 | | |
1454 | | /* -------------------------------------------------------------------- */ |
1455 | | /* Push the last record back on the input queue. */ |
1456 | | /* -------------------------------------------------------------------- */ |
1457 | 0 | if (poRecord != nullptr) |
1458 | 0 | SaveRecord(poRecord); |
1459 | | |
1460 | | /* -------------------------------------------------------------------- */ |
1461 | | /* Return the list, or NULL if we didn't get any records. */ |
1462 | | /* -------------------------------------------------------------------- */ |
1463 | 0 | if (nRecordCount == 0) |
1464 | 0 | return nullptr; |
1465 | 0 | else |
1466 | 0 | return apoCGroup; |
1467 | 0 | } |
1468 | | |
1469 | | /************************************************************************/ |
1470 | | /* GetFeatureClass() */ |
1471 | | /************************************************************************/ |
1472 | | |
1473 | | int NTFFileReader::GetFeatureClass(int iFCIndex, char **ppszFCId, |
1474 | | char **ppszFCName) |
1475 | | |
1476 | 0 | { |
1477 | 0 | if (iFCIndex < 0 || iFCIndex >= nFCCount) |
1478 | 0 | { |
1479 | 0 | *ppszFCId = nullptr; |
1480 | 0 | *ppszFCName = nullptr; |
1481 | 0 | return FALSE; |
1482 | 0 | } |
1483 | 0 | else |
1484 | 0 | { |
1485 | 0 | *ppszFCId = papszFCNum[iFCIndex]; |
1486 | 0 | *ppszFCName = papszFCName[iFCIndex]; |
1487 | 0 | return TRUE; |
1488 | 0 | } |
1489 | 0 | } |
1490 | | |
1491 | | /************************************************************************/ |
1492 | | /* ReadOGRFeature() */ |
1493 | | /************************************************************************/ |
1494 | | |
1495 | | OGRFeature *NTFFileReader::ReadOGRFeature(OGRNTFLayer *poTargetLayer) |
1496 | | |
1497 | 0 | { |
1498 | | /* -------------------------------------------------------------------- */ |
1499 | | /* If this is a raster file, use a custom method to read the */ |
1500 | | /* feature. */ |
1501 | | /* -------------------------------------------------------------------- */ |
1502 | 0 | if (IsRasterProduct()) |
1503 | 0 | return poRasterLayer->GetNextFeature(); |
1504 | | |
1505 | | /* -------------------------------------------------------------------- */ |
1506 | | /* Loop looking for a group we can translate, and that if */ |
1507 | | /* needed matches our layer request. */ |
1508 | | /* -------------------------------------------------------------------- */ |
1509 | 0 | OGRNTFLayer *poLayer = nullptr; |
1510 | 0 | OGRFeature *poFeature = nullptr; |
1511 | |
|
1512 | 0 | while (true) |
1513 | 0 | { |
1514 | 0 | NTFRecord **papoGroup = nullptr; |
1515 | |
|
1516 | 0 | if (GetProductId() == NPC_UNKNOWN && nNTFLevel > 2) |
1517 | 0 | papoGroup = GetNextIndexedRecordGroup(apoCGroup + 1); |
1518 | 0 | else |
1519 | 0 | papoGroup = ReadRecordGroup(); |
1520 | |
|
1521 | 0 | if (papoGroup == nullptr || papoGroup[0] == nullptr) |
1522 | 0 | break; |
1523 | | |
1524 | 0 | int nType = papoGroup[0]->GetType(); |
1525 | 0 | if (nType < 0 || nType >= (int)(sizeof(apoTypeTranslation) / |
1526 | 0 | sizeof(apoTypeTranslation[0]))) |
1527 | 0 | continue; |
1528 | 0 | poLayer = apoTypeTranslation[nType]; |
1529 | 0 | if (poLayer == nullptr) |
1530 | 0 | continue; |
1531 | | |
1532 | 0 | if (poTargetLayer != nullptr && poTargetLayer != poLayer) |
1533 | 0 | { |
1534 | 0 | CacheLineGeometryInGroup(papoGroup); |
1535 | 0 | nSavedFeatureId++; |
1536 | 0 | continue; |
1537 | 0 | } |
1538 | | |
1539 | 0 | poFeature = poLayer->FeatureTranslate(this, papoGroup); |
1540 | 0 | if (poFeature == nullptr) |
1541 | 0 | { |
1542 | | // should this be a real error? |
1543 | 0 | CPLDebug("NTF", |
1544 | 0 | "FeatureTranslate() failed for a type %d record group\n" |
1545 | 0 | "in a %s type file.\n", |
1546 | 0 | papoGroup[0]->GetType(), GetProduct()); |
1547 | 0 | } |
1548 | 0 | else |
1549 | 0 | { |
1550 | 0 | break; |
1551 | 0 | } |
1552 | 0 | } |
1553 | | |
1554 | | /* -------------------------------------------------------------------- */ |
1555 | | /* If we got a feature, set the TILE_REF on it. */ |
1556 | | /* -------------------------------------------------------------------- */ |
1557 | 0 | if (poFeature != nullptr) |
1558 | 0 | { |
1559 | 0 | int iTileRefField = poLayer->GetLayerDefn()->GetFieldCount() - 1; |
1560 | |
|
1561 | 0 | CPLAssert(EQUAL( |
1562 | 0 | poLayer->GetLayerDefn()->GetFieldDefn(iTileRefField)->GetNameRef(), |
1563 | 0 | "TILE_REF")); |
1564 | |
|
1565 | 0 | poFeature->SetField(iTileRefField, GetTileName()); |
1566 | 0 | poFeature->SetFID(nSavedFeatureId); |
1567 | |
|
1568 | 0 | nSavedFeatureId++; |
1569 | 0 | } |
1570 | | |
1571 | | /* -------------------------------------------------------------------- */ |
1572 | | /* If we got to the end we can establish our feature count for */ |
1573 | | /* the file. */ |
1574 | | /* -------------------------------------------------------------------- */ |
1575 | 0 | else |
1576 | 0 | { |
1577 | | // This assertion was triggered by |
1578 | | // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=1982 it doesn't |
1579 | | // look critical enough to be enabled. |
1580 | | // CPLAssert( nFeatureCount == -1 |
1581 | | // || nFeatureCount == nSavedFeatureId - nBaseFeatureId ); |
1582 | 0 | nFeatureCount = nSavedFeatureId - nBaseFeatureId; |
1583 | 0 | } |
1584 | |
|
1585 | 0 | return poFeature; |
1586 | 0 | } |
1587 | | |
1588 | | /************************************************************************/ |
1589 | | /* TestForLayer() */ |
1590 | | /* */ |
1591 | | /* Return indicator of whether this file contains any features */ |
1592 | | /* of the indicated layer type. */ |
1593 | | /************************************************************************/ |
1594 | | |
1595 | | int NTFFileReader::TestForLayer(OGRNTFLayer *poLayer) |
1596 | | |
1597 | 0 | { |
1598 | 0 | for (int i = 0; i < 100; i++) |
1599 | 0 | { |
1600 | 0 | if (apoTypeTranslation[i] == poLayer) |
1601 | 0 | return TRUE; |
1602 | 0 | } |
1603 | | |
1604 | 0 | return FALSE; |
1605 | 0 | } |
1606 | | |
1607 | | /************************************************************************/ |
1608 | | /* FreshenIndex() */ |
1609 | | /* */ |
1610 | | /* Rebuild the index if it is needed, and currently missing. */ |
1611 | | /************************************************************************/ |
1612 | | |
1613 | | void NTFFileReader::FreshenIndex() |
1614 | | |
1615 | 0 | { |
1616 | 0 | if (!bIndexBuilt && bIndexNeeded) |
1617 | 0 | IndexFile(); |
1618 | 0 | } |
1619 | | |
1620 | | /************************************************************************/ |
1621 | | /* IndexFile() */ |
1622 | | /* */ |
1623 | | /* Read all records beyond the section header and build an */ |
1624 | | /* internal index of them. */ |
1625 | | /************************************************************************/ |
1626 | | |
1627 | | void NTFFileReader::IndexFile() |
1628 | | |
1629 | 0 | { |
1630 | 0 | Reset(); |
1631 | |
|
1632 | 0 | DestroyIndex(); |
1633 | |
|
1634 | 0 | bIndexNeeded = TRUE; |
1635 | 0 | bIndexBuilt = TRUE; |
1636 | 0 | bCacheLines = FALSE; |
1637 | | |
1638 | | /* -------------------------------------------------------------------- */ |
1639 | | /* Process all records after the section header, and before 99 */ |
1640 | | /* to put them in the index. */ |
1641 | | /* -------------------------------------------------------------------- */ |
1642 | 0 | NTFRecord *poRecord = nullptr; |
1643 | 0 | while ((poRecord = ReadRecord()) != nullptr && poRecord->GetType() != 99) |
1644 | 0 | { |
1645 | 0 | const int iType = poRecord->GetType(); |
1646 | 0 | const int iId = atoi(poRecord->GetField(3, 8)); |
1647 | |
|
1648 | 0 | if (iType < 0 || iType >= 100) |
1649 | 0 | { |
1650 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1651 | 0 | "Illegal type %d record, skipping.", iType); |
1652 | 0 | delete poRecord; |
1653 | 0 | continue; |
1654 | 0 | } |
1655 | 0 | if (iId < 0) |
1656 | 0 | { |
1657 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1658 | 0 | "Illegal id %d record, skipping.", iId); |
1659 | 0 | delete poRecord; |
1660 | 0 | continue; |
1661 | 0 | } |
1662 | | |
1663 | | /* -------------------------------------------------------------------- |
1664 | | */ |
1665 | | /* Grow type specific subindex if needed. */ |
1666 | | /* -------------------------------------------------------------------- |
1667 | | */ |
1668 | 0 | if (anIndexSize[iType] <= iId) |
1669 | 0 | { |
1670 | 0 | const int nNewSize = std::max(iId + 1, anIndexSize[iType] * 2 + 10); |
1671 | |
|
1672 | 0 | apapoRecordIndex[iType] = static_cast<NTFRecord **>( |
1673 | 0 | CPLRealloc(apapoRecordIndex[iType], sizeof(void *) * nNewSize)); |
1674 | |
|
1675 | 0 | for (int i = anIndexSize[iType]; i < nNewSize; i++) |
1676 | 0 | (apapoRecordIndex[iType])[i] = nullptr; |
1677 | |
|
1678 | 0 | anIndexSize[iType] = nNewSize; |
1679 | 0 | } |
1680 | | |
1681 | | /* -------------------------------------------------------------------- |
1682 | | */ |
1683 | | /* Put record into type specific subindex based on its id as */ |
1684 | | /* the key. */ |
1685 | | /* -------------------------------------------------------------------- |
1686 | | */ |
1687 | 0 | if (apapoRecordIndex[iType][iId] != nullptr) |
1688 | 0 | { |
1689 | 0 | CPLDebug("OGR_NTF", |
1690 | 0 | "Duplicate record with index %d and type %d\n" |
1691 | 0 | "in NTFFileReader::IndexFile().", |
1692 | 0 | iId, iType); |
1693 | 0 | delete apapoRecordIndex[iType][iId]; |
1694 | 0 | } |
1695 | 0 | (apapoRecordIndex[iType])[iId] = poRecord; |
1696 | 0 | } |
1697 | |
|
1698 | 0 | if (poRecord != nullptr) |
1699 | 0 | delete poRecord; |
1700 | 0 | } |
1701 | | |
1702 | | /************************************************************************/ |
1703 | | /* DestroyIndex() */ |
1704 | | /************************************************************************/ |
1705 | | |
1706 | | void NTFFileReader::DestroyIndex() |
1707 | | |
1708 | 2 | { |
1709 | 202 | for (int i = 0; i < 100; i++) |
1710 | 200 | { |
1711 | 200 | for (int iId = 0; iId < anIndexSize[i]; iId++) |
1712 | 0 | { |
1713 | 0 | if ((apapoRecordIndex[i])[iId] != nullptr) |
1714 | 0 | delete (apapoRecordIndex[i])[iId]; |
1715 | 0 | } |
1716 | | |
1717 | 200 | CPLFree(apapoRecordIndex[i]); |
1718 | 200 | apapoRecordIndex[i] = nullptr; |
1719 | 200 | anIndexSize[i] = 0; |
1720 | 200 | } |
1721 | | |
1722 | 2 | bIndexBuilt = FALSE; |
1723 | 2 | } |
1724 | | |
1725 | | /************************************************************************/ |
1726 | | /* GetIndexedRecord() */ |
1727 | | /************************************************************************/ |
1728 | | |
1729 | | NTFRecord *NTFFileReader::GetIndexedRecord(int iType, int iId) |
1730 | | |
1731 | 0 | { |
1732 | 0 | if ((iType < 0 || iType > 99) || (iId < 0 || iId >= anIndexSize[iType]) || |
1733 | 0 | (apapoRecordIndex[iType])[iId] == nullptr) |
1734 | 0 | { |
1735 | | /* If NRT_GEOMETRY3D is an acceptable alternative to 2D */ |
1736 | 0 | if (iType == NRT_GEOMETRY) |
1737 | 0 | return GetIndexedRecord(NRT_GEOMETRY3D, iId); |
1738 | 0 | else |
1739 | 0 | return nullptr; |
1740 | 0 | } |
1741 | | |
1742 | 0 | return (apapoRecordIndex[iType])[iId]; |
1743 | 0 | } |
1744 | | |
1745 | | /************************************************************************/ |
1746 | | /* AddToIndexGroup() */ |
1747 | | /************************************************************************/ |
1748 | | |
1749 | | void NTFFileReader::AddToIndexGroup(NTFRecord *poRecord) |
1750 | | |
1751 | 0 | { |
1752 | 0 | int i = 1; // Used after for. |
1753 | 0 | for (; apoCGroup[i] != nullptr; i++) |
1754 | 0 | { |
1755 | 0 | if (apoCGroup[i] == poRecord) |
1756 | 0 | { |
1757 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1758 | 0 | "Record already inserted in group"); |
1759 | 0 | return; |
1760 | 0 | } |
1761 | 0 | } |
1762 | 0 | if (i == MAX_REC_GROUP) |
1763 | 0 | { |
1764 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1765 | 0 | "Maximum number of records in group reached"); |
1766 | 0 | delete poRecord; |
1767 | 0 | return; |
1768 | 0 | } |
1769 | | |
1770 | 0 | apoCGroup[i] = poRecord; |
1771 | 0 | apoCGroup[i + 1] = nullptr; |
1772 | 0 | } |
1773 | | |
1774 | | /************************************************************************/ |
1775 | | /* GetNextIndexedRecordGroup() */ |
1776 | | /************************************************************************/ |
1777 | | |
1778 | | NTFRecord **NTFFileReader::GetNextIndexedRecordGroup(NTFRecord **papoPrevGroup) |
1779 | | |
1780 | 0 | { |
1781 | 0 | int nPrevType, nPrevId; |
1782 | | |
1783 | | /* -------------------------------------------------------------------- */ |
1784 | | /* What was the identify of our previous anchor record? */ |
1785 | | /* -------------------------------------------------------------------- */ |
1786 | 0 | if (papoPrevGroup == nullptr || papoPrevGroup[0] == nullptr) |
1787 | 0 | { |
1788 | 0 | nPrevType = NRT_POINTREC; |
1789 | 0 | nPrevId = 0; |
1790 | 0 | FreshenIndex(); |
1791 | 0 | } |
1792 | 0 | else |
1793 | 0 | { |
1794 | 0 | nPrevType = papoPrevGroup[0]->GetType(); |
1795 | 0 | nPrevId = atoi(papoPrevGroup[0]->GetField(3, 8)); |
1796 | 0 | if (nPrevId < 0) |
1797 | 0 | return nullptr; |
1798 | 0 | } |
1799 | | |
1800 | | /* -------------------------------------------------------------------- */ |
1801 | | /* Find the next anchor record. */ |
1802 | | /* -------------------------------------------------------------------- */ |
1803 | 0 | NTFRecord *poAnchor = nullptr; |
1804 | |
|
1805 | 0 | while (nPrevType != 99 && poAnchor == nullptr) |
1806 | 0 | { |
1807 | 0 | nPrevId++; |
1808 | 0 | if (nPrevId >= anIndexSize[nPrevType]) |
1809 | 0 | { |
1810 | 0 | do |
1811 | 0 | { |
1812 | 0 | nPrevType++; |
1813 | 0 | } while (nPrevType != NRT_VTR && nPrevType != NRT_NODEREC && |
1814 | 0 | nPrevType != NRT_TEXTREC && nPrevType != NRT_NAMEREC && |
1815 | 0 | nPrevType != NRT_COLLECT && nPrevType != NRT_POLYGON && |
1816 | 0 | nPrevType != NRT_CPOLY && nPrevType != NRT_POINTREC && |
1817 | 0 | nPrevType != NRT_LINEREC); |
1818 | |
|
1819 | 0 | nPrevId = 0; |
1820 | 0 | } |
1821 | 0 | else |
1822 | 0 | { |
1823 | 0 | poAnchor = (apapoRecordIndex[nPrevType])[nPrevId]; |
1824 | 0 | } |
1825 | 0 | } |
1826 | |
|
1827 | 0 | if (poAnchor == nullptr) |
1828 | 0 | { |
1829 | 0 | return nullptr; |
1830 | 0 | } |
1831 | | |
1832 | | /* -------------------------------------------------------------------- */ |
1833 | | /* Build record group depending on type of anchor and what it */ |
1834 | | /* refers to. */ |
1835 | | /* -------------------------------------------------------------------- */ |
1836 | 0 | apoCGroup[0] = nullptr; |
1837 | 0 | apoCGroup[1] = poAnchor; |
1838 | 0 | apoCGroup[2] = nullptr; |
1839 | | |
1840 | | /* -------------------------------------------------------------------- */ |
1841 | | /* Handle POINTREC/LINEREC */ |
1842 | | /* -------------------------------------------------------------------- */ |
1843 | 0 | if (poAnchor->GetType() == NRT_POINTREC || |
1844 | 0 | poAnchor->GetType() == NRT_LINEREC) |
1845 | 0 | { |
1846 | 0 | int l_nAttCount = 0; |
1847 | |
|
1848 | 0 | AddToIndexGroup( |
1849 | 0 | GetIndexedRecord(NRT_GEOMETRY, atoi(poAnchor->GetField(9, 14)))); |
1850 | |
|
1851 | 0 | if (poAnchor->GetLength() >= 16) |
1852 | 0 | l_nAttCount = atoi(poAnchor->GetField(15, 16)); |
1853 | |
|
1854 | 0 | for (int iAtt = 0; iAtt < l_nAttCount; iAtt++) |
1855 | 0 | { |
1856 | 0 | AddToIndexGroup(GetIndexedRecord( |
1857 | 0 | NRT_ATTREC, |
1858 | 0 | atoi(poAnchor->GetField(17 + 6 * iAtt, 22 + 6 * iAtt)))); |
1859 | 0 | } |
1860 | 0 | } |
1861 | | |
1862 | | /* -------------------------------------------------------------------- */ |
1863 | | /* Handle TEXTREC */ |
1864 | | /* -------------------------------------------------------------------- */ |
1865 | 0 | else if (poAnchor->GetType() == NRT_TEXTREC) |
1866 | 0 | { |
1867 | 0 | int l_nAttCount = 0; |
1868 | 0 | int nSelCount = 0; |
1869 | | |
1870 | | // Add all the text position records. |
1871 | 0 | nSelCount = atoi(poAnchor->GetField(9, 10)); |
1872 | 0 | if (nSelCount < 0) |
1873 | 0 | return nullptr; |
1874 | | |
1875 | 0 | for (int iSel = 0; iSel < nSelCount; iSel++) |
1876 | 0 | { |
1877 | 0 | int iStart = 11 + 12 * iSel + 6; |
1878 | |
|
1879 | 0 | AddToIndexGroup(GetIndexedRecord( |
1880 | 0 | NRT_TEXTPOS, atoi(poAnchor->GetField(iStart, iStart + 5)))); |
1881 | 0 | } |
1882 | | |
1883 | | // Add all geometry and TEXR records pointed to by text position |
1884 | | // records. |
1885 | 0 | for (int iRec = 1; apoCGroup[iRec] != nullptr; iRec++) |
1886 | 0 | { |
1887 | 0 | NTFRecord *poRecord = apoCGroup[iRec]; |
1888 | |
|
1889 | 0 | if (poRecord->GetType() != NRT_TEXTPOS) |
1890 | 0 | continue; |
1891 | | |
1892 | 0 | const int nNumTEXR = atoi(poRecord->GetField(9, 10)); |
1893 | 0 | for (int iTEXR = 0; iTEXR < nNumTEXR; iTEXR++) |
1894 | 0 | { |
1895 | 0 | AddToIndexGroup(GetIndexedRecord( |
1896 | 0 | NRT_TEXTREP, atoi(poRecord->GetField(11 + iTEXR * 12, |
1897 | 0 | 16 + iTEXR * 12)))); |
1898 | 0 | AddToIndexGroup(GetIndexedRecord( |
1899 | 0 | NRT_GEOMETRY, atoi(poRecord->GetField(17 + iTEXR * 12, |
1900 | 0 | 22 + iTEXR * 12)))); |
1901 | 0 | } |
1902 | 0 | } |
1903 | | |
1904 | | // Add all the attribute records. |
1905 | 0 | if (poAnchor->GetLength() >= 10 + nSelCount * 12 + 2) |
1906 | 0 | l_nAttCount = atoi( |
1907 | 0 | poAnchor->GetField(11 + nSelCount * 12, 12 + nSelCount * 12)); |
1908 | |
|
1909 | 0 | for (int iAtt = 0; iAtt < l_nAttCount; iAtt++) |
1910 | 0 | { |
1911 | 0 | int iStart = 13 + nSelCount * 12 + 6 * iAtt; |
1912 | |
|
1913 | 0 | AddToIndexGroup(GetIndexedRecord( |
1914 | 0 | NRT_ATTREC, atoi(poAnchor->GetField(iStart, iStart + 5)))); |
1915 | 0 | } |
1916 | 0 | } |
1917 | | |
1918 | | /* -------------------------------------------------------------------- */ |
1919 | | /* Handle NODEREC. */ |
1920 | | /* -------------------------------------------------------------------- */ |
1921 | 0 | else if (poAnchor->GetType() == NRT_NODEREC) |
1922 | 0 | { |
1923 | 0 | AddToIndexGroup( |
1924 | 0 | GetIndexedRecord(NRT_GEOMETRY, atoi(poAnchor->GetField(9, 14)))); |
1925 | 0 | } |
1926 | | |
1927 | | /* -------------------------------------------------------------------- */ |
1928 | | /* Handle COLLECT. */ |
1929 | | /* -------------------------------------------------------------------- */ |
1930 | 0 | else if (poAnchor->GetType() == NRT_COLLECT) |
1931 | 0 | { |
1932 | 0 | const int nParts = atoi(poAnchor->GetField(9, 12)); |
1933 | 0 | if (nParts < 0) |
1934 | 0 | return nullptr; |
1935 | 0 | const int nAttOffset = 13 + nParts * 8; |
1936 | 0 | int l_nAttCount = 0; |
1937 | |
|
1938 | 0 | if (poAnchor->GetLength() > nAttOffset + 2) |
1939 | 0 | l_nAttCount = atoi(poAnchor->GetField(nAttOffset, nAttOffset + 1)); |
1940 | |
|
1941 | 0 | for (int iAtt = 0; iAtt < l_nAttCount; iAtt++) |
1942 | 0 | { |
1943 | 0 | const int iStart = nAttOffset + 2 + iAtt * 6; |
1944 | |
|
1945 | 0 | AddToIndexGroup(GetIndexedRecord( |
1946 | 0 | NRT_ATTREC, atoi(poAnchor->GetField(iStart, iStart + 5)))); |
1947 | 0 | } |
1948 | 0 | } |
1949 | | |
1950 | | /* -------------------------------------------------------------------- */ |
1951 | | /* Handle POLYGON */ |
1952 | | /* -------------------------------------------------------------------- */ |
1953 | 0 | else if (poAnchor->GetType() == NRT_POLYGON) |
1954 | 0 | { |
1955 | 0 | AddToIndexGroup( |
1956 | 0 | GetIndexedRecord(NRT_CHAIN, atoi(poAnchor->GetField(9, 14)))); |
1957 | |
|
1958 | 0 | if (poAnchor->GetLength() >= 20) |
1959 | 0 | AddToIndexGroup(GetIndexedRecord(NRT_GEOMETRY, |
1960 | 0 | atoi(poAnchor->GetField(15, 20)))); |
1961 | | |
1962 | | // Attributes |
1963 | 0 | int l_nAttCount = 0; |
1964 | |
|
1965 | 0 | if (poAnchor->GetLength() >= 22) |
1966 | 0 | l_nAttCount = atoi(poAnchor->GetField(21, 22)); |
1967 | |
|
1968 | 0 | for (int iAtt = 0; iAtt < l_nAttCount; iAtt++) |
1969 | 0 | { |
1970 | 0 | AddToIndexGroup(GetIndexedRecord( |
1971 | 0 | NRT_ATTREC, |
1972 | 0 | atoi(poAnchor->GetField(23 + 6 * iAtt, 28 + 6 * iAtt)))); |
1973 | 0 | } |
1974 | 0 | } |
1975 | | /* -------------------------------------------------------------------- */ |
1976 | | /* Handle CPOLY */ |
1977 | | /* -------------------------------------------------------------------- */ |
1978 | 0 | else if (poAnchor->GetType() == NRT_CPOLY) |
1979 | 0 | { |
1980 | 0 | int nPolyCount = atoi(poAnchor->GetField(9, 12)); |
1981 | 0 | if (nPolyCount < 0) |
1982 | 0 | return nullptr; |
1983 | 0 | int nPostPoly = nPolyCount * 7 + 12; |
1984 | |
|
1985 | 0 | if (poAnchor->GetLength() >= nPostPoly + 6) |
1986 | 0 | { |
1987 | 0 | int nGeomId = |
1988 | 0 | atoi(poAnchor->GetField(nPostPoly + 1, nPostPoly + 6)); |
1989 | |
|
1990 | 0 | AddToIndexGroup(GetIndexedRecord(NRT_GEOMETRY, nGeomId)); |
1991 | 0 | } |
1992 | |
|
1993 | 0 | if (poAnchor->GetLength() >= nPostPoly + 8) |
1994 | 0 | { |
1995 | 0 | int l_nAttCount = |
1996 | 0 | atoi(poAnchor->GetField(nPostPoly + 7, nPostPoly + 8)); |
1997 | |
|
1998 | 0 | for (int iAtt = 0; iAtt < l_nAttCount; iAtt++) |
1999 | 0 | { |
2000 | 0 | int nAttId = atoi(poAnchor->GetField( |
2001 | 0 | nPostPoly + 9 + iAtt * 6, nPostPoly + 14 + iAtt * 6)); |
2002 | 0 | AddToIndexGroup(GetIndexedRecord(NRT_ATTREC, nAttId)); |
2003 | 0 | } |
2004 | 0 | } |
2005 | 0 | } |
2006 | | |
2007 | 0 | return apoCGroup + 1; |
2008 | 0 | } |
2009 | | |
2010 | | /************************************************************************/ |
2011 | | /* OverrideTileName() */ |
2012 | | /************************************************************************/ |
2013 | | |
2014 | | void NTFFileReader::OverrideTileName(const char *pszNewName) |
2015 | | |
2016 | 0 | { |
2017 | 0 | CPLFree(pszTileName); |
2018 | 0 | pszTileName = CPLStrdup(pszNewName); |
2019 | 0 | } |
2020 | | |
2021 | | /************************************************************************/ |
2022 | | /* CacheAddByGeomId() */ |
2023 | | /* */ |
2024 | | /* Add a geometry to the geometry cache given its GEOMID as */ |
2025 | | /* the index. */ |
2026 | | /************************************************************************/ |
2027 | | |
2028 | | void NTFFileReader::CacheAddByGeomId(int nGeomId, OGRGeometry *poGeometry) |
2029 | | |
2030 | 0 | { |
2031 | 0 | if (!bCacheLines) |
2032 | 0 | return; |
2033 | | |
2034 | 0 | CPLAssert(nGeomId >= 0); |
2035 | | |
2036 | | /* -------------------------------------------------------------------- */ |
2037 | | /* Grow the cache if it isn't large enough to hold the newly */ |
2038 | | /* requested geometry id. */ |
2039 | | /* -------------------------------------------------------------------- */ |
2040 | 0 | if (nGeomId >= nLineCacheSize) |
2041 | 0 | { |
2042 | 0 | const int nNewSize = nGeomId + 100; |
2043 | |
|
2044 | 0 | papoLineCache = static_cast<OGRGeometry **>( |
2045 | 0 | CPLRealloc(papoLineCache, sizeof(void *) * nNewSize)); |
2046 | 0 | memset(papoLineCache + nLineCacheSize, 0, |
2047 | 0 | sizeof(void *) * (nNewSize - nLineCacheSize)); |
2048 | 0 | nLineCacheSize = nNewSize; |
2049 | 0 | } |
2050 | | |
2051 | | /* -------------------------------------------------------------------- */ |
2052 | | /* Make a cloned copy of the geometry for the cache. */ |
2053 | | /* -------------------------------------------------------------------- */ |
2054 | 0 | if (papoLineCache[nGeomId] != nullptr) |
2055 | 0 | return; |
2056 | | |
2057 | 0 | papoLineCache[nGeomId] = poGeometry->clone(); |
2058 | 0 | } |
2059 | | |
2060 | | /************************************************************************/ |
2061 | | /* CacheGetByGeomId() */ |
2062 | | /************************************************************************/ |
2063 | | |
2064 | | OGRGeometry *NTFFileReader::CacheGetByGeomId(int nGeomId) |
2065 | | |
2066 | 0 | { |
2067 | 0 | if (nGeomId < 0 || nGeomId >= nLineCacheSize) |
2068 | 0 | return nullptr; |
2069 | 0 | else |
2070 | 0 | return papoLineCache[nGeomId]; |
2071 | 0 | } |
2072 | | |
2073 | | /************************************************************************/ |
2074 | | /* CacheClean() */ |
2075 | | /************************************************************************/ |
2076 | | |
2077 | | void NTFFileReader::CacheClean() |
2078 | | |
2079 | 6 | { |
2080 | 6 | for (int i = 0; i < nLineCacheSize; i++) |
2081 | 0 | { |
2082 | 0 | if (papoLineCache[i] != nullptr) |
2083 | 0 | delete papoLineCache[i]; |
2084 | 0 | } |
2085 | 6 | if (papoLineCache != nullptr) |
2086 | 0 | CPLFree(papoLineCache); |
2087 | | |
2088 | 6 | nLineCacheSize = 0; |
2089 | 6 | papoLineCache = nullptr; |
2090 | 6 | } |
2091 | | |
2092 | | /************************************************************************/ |
2093 | | /* CacheLineGeometryInGroup() */ |
2094 | | /* */ |
2095 | | /* Run any line geometries in this group through the */ |
2096 | | /* ProcessGeometry() call just to ensure the line geometry will */ |
2097 | | /* be cached. */ |
2098 | | /************************************************************************/ |
2099 | | |
2100 | | void NTFFileReader::CacheLineGeometryInGroup(NTFRecord **papoGroup) |
2101 | | |
2102 | 0 | { |
2103 | 0 | if (!bCacheLines) |
2104 | 0 | return; |
2105 | | |
2106 | 0 | for (int iRec = 0; papoGroup[iRec] != nullptr; iRec++) |
2107 | 0 | { |
2108 | 0 | if (papoGroup[iRec]->GetType() == NRT_GEOMETRY || |
2109 | 0 | papoGroup[iRec]->GetType() == NRT_GEOMETRY3D) |
2110 | 0 | { |
2111 | 0 | OGRGeometry *poGeom = ProcessGeometry(papoGroup[iRec], nullptr); |
2112 | 0 | if (poGeom != nullptr) |
2113 | 0 | delete poGeom; |
2114 | 0 | } |
2115 | 0 | } |
2116 | 0 | } |
2117 | | |
2118 | | /************************************************************************/ |
2119 | | /* FormPolygonFromCache() */ |
2120 | | /* */ |
2121 | | /* This method will attempt to find the line geometries */ |
2122 | | /* referenced by the GEOM_ID_OF_LINK ids of a feature in the */ |
2123 | | /* line cache (if available), and if so, assemble them into a */ |
2124 | | /* polygon. */ |
2125 | | /************************************************************************/ |
2126 | | |
2127 | | int NTFFileReader::FormPolygonFromCache(OGRFeature *poFeature) |
2128 | | |
2129 | 0 | { |
2130 | 0 | if (!bCacheLines) |
2131 | 0 | return FALSE; |
2132 | | |
2133 | | /* -------------------------------------------------------------------- */ |
2134 | | /* Collect all the linked lines. */ |
2135 | | /* -------------------------------------------------------------------- */ |
2136 | 0 | int nLinkCount = 0; |
2137 | 0 | const int *panLinks = |
2138 | 0 | poFeature->GetFieldAsIntegerList("GEOM_ID_OF_LINK", &nLinkCount); |
2139 | |
|
2140 | 0 | if (panLinks == nullptr) |
2141 | 0 | return FALSE; |
2142 | | |
2143 | 0 | OGRGeometryCollection oLines; |
2144 | |
|
2145 | 0 | for (int i = 0; i < nLinkCount; i++) |
2146 | 0 | { |
2147 | 0 | OGRGeometry *poLine = CacheGetByGeomId(panLinks[i]); |
2148 | 0 | if (poLine == nullptr) |
2149 | 0 | { |
2150 | 0 | oLines.removeGeometry(-1, FALSE); |
2151 | 0 | return FALSE; |
2152 | 0 | } |
2153 | | |
2154 | 0 | oLines.addGeometryDirectly(poLine); |
2155 | 0 | } |
2156 | | |
2157 | | /* -------------------------------------------------------------------- */ |
2158 | | /* Assemble into a polygon geometry. */ |
2159 | | /* -------------------------------------------------------------------- */ |
2160 | 0 | OGRGeometry *poGeom = OGRGeometry::FromHandle(OGRBuildPolygonFromEdges( |
2161 | 0 | (OGRGeometryH)&oLines, FALSE, FALSE, 0.1, nullptr)); |
2162 | |
|
2163 | 0 | poFeature->SetGeometryDirectly(poGeom); |
2164 | |
|
2165 | 0 | oLines.removeGeometry(-1, FALSE); |
2166 | |
|
2167 | 0 | return poGeom != nullptr; |
2168 | 0 | } |